mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-19 19:42:17 +02:00
cleanup: Hungarian Notation - all but 5 files (#15181)
* cleanup: AddContentApi - Hungarian Notation * cleanup: SharedDecksDownloadFragment - Hungarian Notation * cleanup: AbstractFlashcardViewer - Hungarian Notation * cleanup: Reviewer - Hungarian Notation * cleanup: RecursivePictureMenu - Hungarian Notation * cleanup: FieldEditLine - Hungarian Notation * cleanup: ActivityExportingDelegate - Hungarian Notation * cleanup: DeckPicker - Hungarian Notation * cleanup: MultimediaEditFieldActivity - Hungarian Notation * cleanup: FieldControllerBase - Hungarian Notation * cleanup: AnkiDroidJsAPI - Hungarian Notation * cleanup: DeckAdapter - Hungarian Notation * cleanup: Previewer - Hungarian Notation * cleanup: CardTemplatePreviewer - Hungarian Notation * cleanup: BasicImageFieldController - Hungarian Notation * cleanup: AudioRecorder - Hungarian Notation * cleanup: AppCompatPreferenceActivity - Hungarian Notation * cleanup: AudioField - Hungarian Notation * cleanup: Collection - Hungarian Notation * cleanup: MultimediaEditableNote - Hungarian Notation * cleanup: CardTemplateNotetype - Hungarian Notation * cleanup: Card - Hungarian Notation * cleanup: BasicTextFieldController - Hungarian Notation
This commit is contained in:
parent
240d9d8f55
commit
5f2cc77db6
@ -121,15 +121,15 @@ abstract class AbstractFlashcardViewer :
|
||||
OnPageFinishedCallback,
|
||||
BaseSnackbarBuilderProvider,
|
||||
ChangeManager.Subscriber {
|
||||
private var mTtsInitialized = false
|
||||
private var mReplayOnTtsInit = false
|
||||
private var mAnkiDroidJsAPI: AnkiDroidJsAPI? = null
|
||||
private var ttsInitialized = false
|
||||
private var replayOnTtsInit = false
|
||||
private var ankiDroidJsAPI: AnkiDroidJsAPI? = null
|
||||
|
||||
/**
|
||||
* Broadcast that informs us when the sd card is about to be unmounted
|
||||
*/
|
||||
private var mUnmountReceiver: BroadcastReceiver? = null
|
||||
private var mTagsDialogFactory: TagsDialogFactory? = null
|
||||
private var unmountReceiver: BroadcastReceiver? = null
|
||||
private var tagsDialogFactory: TagsDialogFactory? = null
|
||||
|
||||
/**
|
||||
* Variables to hold preferences
|
||||
@ -138,13 +138,13 @@ abstract class AbstractFlashcardViewer :
|
||||
internal var prefShowTopbar = false
|
||||
protected var fullscreenMode = DEFAULT
|
||||
private set
|
||||
private var mRelativeButtonSize = 0
|
||||
private var relativeButtonSize = 0
|
||||
private var minimalClickSpeed = 0
|
||||
private var mDoubleScrolling = false
|
||||
private var mGesturesEnabled = false
|
||||
private var mLargeAnswerButtons = false
|
||||
protected var mAnswerButtonsPosition: String? = "bottom"
|
||||
private var mDoubleTapTimeInterval = DEFAULT_DOUBLE_TAP_TIME_INTERVAL
|
||||
private var doubleScrolling = false
|
||||
private var gesturesEnabled = false
|
||||
private var largeAnswerButtons = false
|
||||
protected var answerButtonsPosition: String? = "bottom"
|
||||
private var doubleTapTimeInterval = DEFAULT_DOUBLE_TAP_TIME_INTERVAL
|
||||
|
||||
// Android WebView
|
||||
var automaticAnswer = AutomaticAnswer.defaultInstance(this)
|
||||
@ -153,24 +153,24 @@ abstract class AbstractFlashcardViewer :
|
||||
internal var typeAnswer: TypeAnswer? = null
|
||||
|
||||
/** Generates HTML content */
|
||||
private var mHtmlGenerator: HtmlGenerator? = null
|
||||
private var htmlGenerator: HtmlGenerator? = null
|
||||
|
||||
// Default short animation duration, provided by Android framework
|
||||
private var shortAnimDuration = 0
|
||||
private var mBackButtonPressedToReturn = false
|
||||
private var backButtonPressedToReturn = false
|
||||
|
||||
// Preferences from the collection
|
||||
private var mShowNextReviewTime = false
|
||||
private var mIsSelecting = false
|
||||
private var mInAnswer = false
|
||||
private var showNextReviewTime = false
|
||||
private var isSelecting = false
|
||||
private var inAnswer = false
|
||||
|
||||
/**
|
||||
* Variables to hold layout objects that we need to update or handle events for
|
||||
*/
|
||||
var webView: WebView? = null
|
||||
private set
|
||||
private var mCardFrame: FrameLayout? = null
|
||||
private var mTouchLayer: FrameLayout? = null
|
||||
private var cardFrame: FrameLayout? = null
|
||||
private var touchLayer: FrameLayout? = null
|
||||
protected var answerField: FixedEditText? = null
|
||||
protected var flipCardLayout: FrameLayout? = null
|
||||
private var easeButtonsLayout: LinearLayout? = null
|
||||
@ -187,12 +187,12 @@ abstract class AbstractFlashcardViewer :
|
||||
@KotlinCleanup("internal for AnkiDroidJsApi")
|
||||
internal var easeButton4: EaseButton? = null
|
||||
protected var topBarLayout: RelativeLayout? = null
|
||||
private val mClipboard: ClipboardManager? = null
|
||||
private var mPreviousAnswerIndicator: PreviousAnswerIndicator? = null
|
||||
private val clipboard: ClipboardManager? = null
|
||||
private var previousAnswerIndicator: PreviousAnswerIndicator? = null
|
||||
|
||||
private var mCurrentEase = 0
|
||||
private var mInitialFlipCardHeight = 0
|
||||
private var mButtonHeightSet = false
|
||||
private var currentEase = 0
|
||||
private var initialFlipCardHeight = 0
|
||||
private var buttonHeightSet = false
|
||||
|
||||
/**
|
||||
* A record of the last time the "show answer" or ease buttons were pressed. We keep track
|
||||
@ -206,14 +206,14 @@ abstract class AbstractFlashcardViewer :
|
||||
*/
|
||||
var gestureDetector: GestureDetector? = null
|
||||
private set
|
||||
private lateinit var mGestureDetectorImpl: MyGestureDetector
|
||||
private var mIsXScrolling = false
|
||||
private var mIsYScrolling = false
|
||||
private lateinit var gestureDetectorImpl: MyGestureDetector
|
||||
private var isXScrolling = false
|
||||
private var isYScrolling = false
|
||||
|
||||
/**
|
||||
* Gesture Allocation
|
||||
*/
|
||||
protected val mGestureProcessor = GestureProcessor(this)
|
||||
protected val gestureProcessor = GestureProcessor(this)
|
||||
|
||||
/** Handle joysticks/pedals */
|
||||
// needs to be lateinit due to a reliance on Context
|
||||
@ -224,36 +224,36 @@ abstract class AbstractFlashcardViewer :
|
||||
private set
|
||||
open val baseUrl = "http://$LOCALHOST"
|
||||
open val webviewDomain = LOCALHOST
|
||||
private var mViewerUrl: String? = null
|
||||
private val mFadeDuration = 300
|
||||
private var viewerUrl: String? = null
|
||||
private val fadeDuration = 300
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
|
||||
internal lateinit var soundPlayer: SoundPlayer
|
||||
|
||||
/** Reference to the parent of the cardFrame to allow regeneration of the cardFrame in case of crash */
|
||||
private var mCardFrameParent: ViewGroup? = null
|
||||
private var cardFrameParent: ViewGroup? = null
|
||||
|
||||
/** Lock to allow thread-safe regeneration of mCard */
|
||||
private val mCardLock: ReadWriteLock = ReentrantReadWriteLock()
|
||||
private val cardLock: ReadWriteLock = ReentrantReadWriteLock()
|
||||
|
||||
/** Preference: Whether the user wants press back twice to return to the main screen" */
|
||||
private var mExitViaDoubleTapBack = false
|
||||
private var exitViaDoubleTapBack = false
|
||||
|
||||
@VisibleForTesting
|
||||
val mOnRenderProcessGoneDelegate = OnRenderProcessGoneDelegate(this)
|
||||
protected val mTTS = TTS()
|
||||
val onRenderProcessGoneDelegate = OnRenderProcessGoneDelegate(this)
|
||||
protected val tts = TTS()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// LISTENERS
|
||||
// ----------------------------------------------------------------------------
|
||||
private val mLongClickHandler = newHandler()
|
||||
private val mStartLongClickAction = Runnable { mGestureProcessor.onLongTap() }
|
||||
private val longClickHandler = newHandler()
|
||||
private val startLongClickAction = Runnable { gestureProcessor.onLongTap() }
|
||||
|
||||
// Handler for the "show answer" button
|
||||
private val mFlipCardListener = View.OnClickListener {
|
||||
private val flipCardListener = View.OnClickListener {
|
||||
Timber.i("AbstractFlashcardViewer:: Show answer button pressed")
|
||||
// Ignore what is most likely an accidental double-tap.
|
||||
if (elapsedRealTime - lastClickTime < mDoubleTapTimeInterval) {
|
||||
if (elapsedRealTime - lastClickTime < doubleTapTimeInterval) {
|
||||
return@OnClickListener
|
||||
}
|
||||
lastClickTime = elapsedRealTime
|
||||
@ -316,27 +316,27 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
// Event handler for eases (answer buttons)
|
||||
inner class SelectEaseHandler : View.OnClickListener, OnTouchListener {
|
||||
private var mPrevCard: Card? = null
|
||||
private var mHasBeenTouched = false
|
||||
private var mTouchX = 0f
|
||||
private var mTouchY = 0f
|
||||
private var prevCard: Card? = null
|
||||
private var hasBeenTouched = false
|
||||
private var touchX = 0f
|
||||
private var touchY = 0f
|
||||
override fun onTouch(view: View, event: MotionEvent): Boolean {
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
// Save states when button pressed
|
||||
mPrevCard = currentCard
|
||||
mHasBeenTouched = true
|
||||
prevCard = currentCard
|
||||
hasBeenTouched = true
|
||||
// We will need to check if a touch is followed by a click
|
||||
// Since onTouch always come before onClick, we should check if
|
||||
// the touch is going to be a click by storing the start coordinates
|
||||
// and comparing with the end coordinates of the touch
|
||||
mTouchX = event.rawX
|
||||
mTouchY = event.rawY
|
||||
touchX = event.rawX
|
||||
touchY = event.rawY
|
||||
} else if (event.action == MotionEvent.ACTION_UP) {
|
||||
val diffX = abs(event.rawX - mTouchX)
|
||||
val diffY = abs(event.rawY - mTouchY)
|
||||
val diffX = abs(event.rawX - touchX)
|
||||
val diffY = abs(event.rawY - touchY)
|
||||
// If a click is not coming then we reset the touch
|
||||
if (diffX > Companion.CLICK_ACTION_THRESHOLD || diffY > Companion.CLICK_ACTION_THRESHOLD) {
|
||||
mHasBeenTouched = false
|
||||
hasBeenTouched = false
|
||||
}
|
||||
}
|
||||
return false
|
||||
@ -345,11 +345,11 @@ abstract class AbstractFlashcardViewer :
|
||||
override fun onClick(view: View) {
|
||||
// Try to perform intended action only if the button has been pressed for current card,
|
||||
// or if the button was not touched,
|
||||
if (mPrevCard === currentCard || !mHasBeenTouched) {
|
||||
if (prevCard === currentCard || !hasBeenTouched) {
|
||||
// Only perform if the click was not an accidental double-tap
|
||||
if (elapsedRealTime - lastClickTime >= mDoubleTapTimeInterval) {
|
||||
if (elapsedRealTime - lastClickTime >= doubleTapTimeInterval) {
|
||||
// For whatever reason, performClick does not return a visual feedback anymore
|
||||
if (!mHasBeenTouched) {
|
||||
if (!hasBeenTouched) {
|
||||
view.isPressed = true
|
||||
}
|
||||
lastClickTime = elapsedRealTime
|
||||
@ -375,29 +375,29 @@ abstract class AbstractFlashcardViewer :
|
||||
answerCard(Consts.BUTTON_FOUR)
|
||||
}
|
||||
|
||||
else -> mCurrentEase = 0
|
||||
else -> currentEase = 0
|
||||
}
|
||||
if (!mHasBeenTouched) {
|
||||
if (!hasBeenTouched) {
|
||||
view.isPressed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
// We will have to reset the touch after every onClick event
|
||||
// Do not return early without considering this
|
||||
mHasBeenTouched = false
|
||||
hasBeenTouched = false
|
||||
}
|
||||
}
|
||||
|
||||
private val mEaseHandler = SelectEaseHandler()
|
||||
private val easeHandler = SelectEaseHandler()
|
||||
|
||||
@get:VisibleForTesting
|
||||
protected open val elapsedRealTime: Long
|
||||
get() = SystemClock.elapsedRealtime()
|
||||
private val mGestureListener = OnTouchListener { _, event ->
|
||||
private val gestureListener = OnTouchListener { _, event ->
|
||||
if (gestureDetector!!.onTouchEvent(event)) {
|
||||
return@OnTouchListener true
|
||||
}
|
||||
if (!mGestureDetectorImpl.eventCanBeSentToWebView(event)) {
|
||||
if (!gestureDetectorImpl.eventCanBeSentToWebView(event)) {
|
||||
return@OnTouchListener false
|
||||
}
|
||||
// Gesture listener is added before mCard is set
|
||||
@ -419,7 +419,7 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
@CheckResult
|
||||
private fun <T> processCardFunction(cardFunction: Function<WebView?, T>): T {
|
||||
val readLock = mCardLock.readLock()
|
||||
val readLock = cardLock.readLock()
|
||||
return try {
|
||||
readLock.lock()
|
||||
cardFunction.apply(webView)
|
||||
@ -514,7 +514,7 @@ abstract class AbstractFlashcardViewer :
|
||||
// ----------------------------------------------------------------------------
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
restorePreferences()
|
||||
mTagsDialogFactory = TagsDialogFactory(this).attachToActivity<TagsDialogFactory>(this)
|
||||
tagsDialogFactory = TagsDialogFactory(this).attachToActivity<TagsDialogFactory>(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
motionEventHandler = MotionEventHandler.createInstance(this)
|
||||
|
||||
@ -528,9 +528,9 @@ abstract class AbstractFlashcardViewer :
|
||||
delegate.isHandleNativeActionModesEnabled = true
|
||||
val mainView = findViewById<View>(android.R.id.content)
|
||||
initNavigationDrawer(mainView)
|
||||
mPreviousAnswerIndicator = PreviousAnswerIndicator(findViewById(R.id.chosen_answer))
|
||||
previousAnswerIndicator = PreviousAnswerIndicator(findViewById(R.id.chosen_answer))
|
||||
shortAnimDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||
mGestureDetectorImpl = LinkDetectingGestureDetector()
|
||||
gestureDetectorImpl = LinkDetectingGestureDetector()
|
||||
TtsVoicesFieldFilter.ensureApplied()
|
||||
if (!sharedPrefs().getBoolean("showDeckTitle", false)) {
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
@ -559,10 +559,10 @@ abstract class AbstractFlashcardViewer :
|
||||
registerExternalStorageListener()
|
||||
restoreCollectionPreferences(col)
|
||||
initLayout()
|
||||
mHtmlGenerator = createInstance(this, col, typeAnswer!!)
|
||||
htmlGenerator = createInstance(this, col, typeAnswer!!)
|
||||
|
||||
// Initialize text-to-speech. This is an asynchronous operation.
|
||||
mTTS.initialize(this, ReadTextListener())
|
||||
tts.initialize(this, ReadTextListener())
|
||||
updateActionBar()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
@ -586,8 +586,8 @@ abstract class AbstractFlashcardViewer :
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
automaticAnswer.disable()
|
||||
mGestureDetectorImpl.stopShakeDetector()
|
||||
mLongClickHandler.removeCallbacks(mStartLongClickAction)
|
||||
gestureDetectorImpl.stopShakeDetector()
|
||||
longClickHandler.removeCallbacks(startLongClickAction)
|
||||
// Prevent loss of data in Cookies
|
||||
CookieManager.getInstance().flush()
|
||||
}
|
||||
@ -595,7 +595,7 @@ abstract class AbstractFlashcardViewer :
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
automaticAnswer.enable()
|
||||
mGestureDetectorImpl.startShakeDetector()
|
||||
gestureDetectorImpl.startShakeDetector()
|
||||
// Reset the activity title
|
||||
updateActionBar()
|
||||
selectNavigationItem(-1)
|
||||
@ -627,14 +627,14 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mTTS.releaseTts(this)
|
||||
if (mUnmountReceiver != null) {
|
||||
unregisterReceiver(mUnmountReceiver)
|
||||
tts.releaseTts(this)
|
||||
if (unmountReceiver != null) {
|
||||
unregisterReceiver(unmountReceiver)
|
||||
}
|
||||
// WebView.destroy() should be called after the end of use
|
||||
// http://developer.android.com/reference/android/webkit/WebView.html#destroy()
|
||||
if (mCardFrame != null) {
|
||||
mCardFrame!!.removeAllViews()
|
||||
if (cardFrame != null) {
|
||||
cardFrame!!.removeAllViews()
|
||||
}
|
||||
destroyWebView(webView) // OK to do without a lock
|
||||
}
|
||||
@ -644,14 +644,14 @@ abstract class AbstractFlashcardViewer :
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
Timber.i("Back key pressed")
|
||||
if (!mExitViaDoubleTapBack || mBackButtonPressedToReturn) {
|
||||
if (!exitViaDoubleTapBack || backButtonPressedToReturn) {
|
||||
closeReviewer(RESULT_DEFAULT)
|
||||
} else {
|
||||
showSnackbar(R.string.back_pressed_once_reviewer, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
mBackButtonPressedToReturn = true
|
||||
backButtonPressedToReturn = true
|
||||
executeFunctionWithDelay(Consts.SHORT_TOAST_DURATION) {
|
||||
mBackButtonPressedToReturn = false
|
||||
backButtonPressedToReturn = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -695,14 +695,14 @@ abstract class AbstractFlashcardViewer :
|
||||
private fun processHardwareButtonScroll(keyCode: Int, card: WebView?): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
|
||||
card!!.pageUp(false)
|
||||
if (mDoubleScrolling) {
|
||||
if (doubleScrolling) {
|
||||
card.pageUp(false)
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
|
||||
card!!.pageDown(false)
|
||||
if (mDoubleScrolling) {
|
||||
if (doubleScrolling) {
|
||||
card.pageDown(false)
|
||||
}
|
||||
return true
|
||||
@ -715,7 +715,7 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
protected fun clipboardHasText(): Boolean {
|
||||
return !getText(mClipboard).isNullOrEmpty()
|
||||
return !getText(clipboard).isNullOrEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -725,7 +725,7 @@ abstract class AbstractFlashcardViewer :
|
||||
* @return the text in clipboard or the empty string.
|
||||
*/
|
||||
private fun clipboardGetText(): CharSequence {
|
||||
val text = getText(mClipboard)
|
||||
val text = getText(clipboard)
|
||||
return text ?: ""
|
||||
}
|
||||
|
||||
@ -779,8 +779,8 @@ abstract class AbstractFlashcardViewer :
|
||||
* Show/dismiss dialog when sd card is ejected/remounted (collection is saved by SdCardReceiver)
|
||||
*/
|
||||
private fun registerExternalStorageListener() {
|
||||
if (mUnmountReceiver == null) {
|
||||
mUnmountReceiver = object : BroadcastReceiver() {
|
||||
if (unmountReceiver == null) {
|
||||
unmountReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (intent.action == SdCardReceiver.MEDIA_EJECT) {
|
||||
finish()
|
||||
@ -789,7 +789,7 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
val iFilter = IntentFilter()
|
||||
iFilter.addAction(SdCardReceiver.MEDIA_EJECT)
|
||||
registerReceiver(mUnmountReceiver, iFilter)
|
||||
registerReceiver(unmountReceiver, iFilter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -861,18 +861,18 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
open fun answerCard(@BUTTON_TYPE ease: Int) {
|
||||
launchCatchingTask {
|
||||
if (mInAnswer) {
|
||||
if (inAnswer) {
|
||||
return@launchCatchingTask
|
||||
}
|
||||
mIsSelecting = false
|
||||
if (mPreviousAnswerIndicator == null) {
|
||||
isSelecting = false
|
||||
if (previousAnswerIndicator == null) {
|
||||
// workaround for a broken ReviewerKeyboardInputTest
|
||||
return@launchCatchingTask
|
||||
}
|
||||
// Temporarily sets the answer indicator dots appearing below the toolbar
|
||||
mPreviousAnswerIndicator?.displayAnswerIndicator(ease)
|
||||
previousAnswerIndicator?.displayAnswerIndicator(ease)
|
||||
soundPlayer.stopSounds()
|
||||
mCurrentEase = ease
|
||||
currentEase = ease
|
||||
|
||||
answerCardInner(ease)
|
||||
updateCardAndRedraw()
|
||||
@ -890,40 +890,40 @@ abstract class AbstractFlashcardViewer :
|
||||
@KotlinCleanup("Move a lot of these to onCreate()")
|
||||
protected open fun initLayout() {
|
||||
topBarLayout = findViewById(R.id.top_bar)
|
||||
mCardFrame = findViewById(R.id.flashcard)
|
||||
mCardFrameParent = mCardFrame!!.parent as ViewGroup
|
||||
mTouchLayer =
|
||||
findViewById<FrameLayout>(R.id.touch_layer).apply { setOnTouchListener(mGestureListener) }
|
||||
mCardFrame!!.removeAllViews()
|
||||
cardFrame = findViewById(R.id.flashcard)
|
||||
cardFrameParent = cardFrame!!.parent as ViewGroup
|
||||
touchLayer =
|
||||
findViewById<FrameLayout>(R.id.touch_layer).apply { setOnTouchListener(gestureListener) }
|
||||
cardFrame!!.removeAllViews()
|
||||
|
||||
// Initialize swipe
|
||||
gestureDetector = GestureDetector(this, mGestureDetectorImpl)
|
||||
gestureDetector = GestureDetector(this, gestureDetectorImpl)
|
||||
easeButtonsLayout = findViewById(R.id.ease_buttons)
|
||||
easeButton1 = EaseButton(
|
||||
EASE_1,
|
||||
findViewById(R.id.flashcard_layout_ease1),
|
||||
findViewById(R.id.ease1),
|
||||
findViewById(R.id.nextTime1)
|
||||
).apply { setListeners(mEaseHandler) }
|
||||
).apply { setListeners(easeHandler) }
|
||||
easeButton2 = EaseButton(
|
||||
EASE_2,
|
||||
findViewById(R.id.flashcard_layout_ease2),
|
||||
findViewById(R.id.ease2),
|
||||
findViewById(R.id.nextTime2)
|
||||
).apply { setListeners(mEaseHandler) }
|
||||
).apply { setListeners(easeHandler) }
|
||||
easeButton3 = EaseButton(
|
||||
EASE_3,
|
||||
findViewById(R.id.flashcard_layout_ease3),
|
||||
findViewById(R.id.ease3),
|
||||
findViewById(R.id.nextTime3)
|
||||
).apply { setListeners(mEaseHandler) }
|
||||
).apply { setListeners(easeHandler) }
|
||||
easeButton4 = EaseButton(
|
||||
EASE_4,
|
||||
findViewById(R.id.flashcard_layout_ease4),
|
||||
findViewById(R.id.ease4),
|
||||
findViewById(R.id.nextTime4)
|
||||
).apply { setListeners(mEaseHandler) }
|
||||
if (!mShowNextReviewTime) {
|
||||
).apply { setListeners(easeHandler) }
|
||||
if (!showNextReviewTime) {
|
||||
easeButton1!!.hideNextReviewTime()
|
||||
easeButton2!!.hideNextReviewTime()
|
||||
easeButton3!!.hideNextReviewTime()
|
||||
@ -932,14 +932,14 @@ abstract class AbstractFlashcardViewer :
|
||||
flipCardLayout = findViewById(R.id.flashcard_layout_flip)
|
||||
flipCardLayout?.let { layout ->
|
||||
if (minimalClickSpeed == 0) {
|
||||
layout.setOnClickListener(mFlipCardListener)
|
||||
layout.setOnClickListener(flipCardListener)
|
||||
} else {
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
layout.setOnTouchListener { _, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
handler.postDelayed({
|
||||
mFlipCardListener.onClick(layout)
|
||||
flipCardListener.onClick(layout)
|
||||
}, minimalClickSpeed.toLong())
|
||||
false
|
||||
}
|
||||
@ -957,19 +957,19 @@ abstract class AbstractFlashcardViewer :
|
||||
if (animationEnabled()) {
|
||||
flipCardLayout?.setBackgroundResource(getResFromAttr(this, R.attr.hardButtonRippleRef))
|
||||
}
|
||||
if (!mButtonHeightSet && mRelativeButtonSize != 100) {
|
||||
if (!buttonHeightSet && relativeButtonSize != 100) {
|
||||
val params = flipCardLayout!!.layoutParams
|
||||
params.height = params.height * mRelativeButtonSize / 100
|
||||
easeButton1!!.setButtonScale(mRelativeButtonSize)
|
||||
easeButton2!!.setButtonScale(mRelativeButtonSize)
|
||||
easeButton3!!.setButtonScale(mRelativeButtonSize)
|
||||
easeButton4!!.setButtonScale(mRelativeButtonSize)
|
||||
mButtonHeightSet = true
|
||||
params.height = params.height * relativeButtonSize / 100
|
||||
easeButton1!!.setButtonScale(relativeButtonSize)
|
||||
easeButton2!!.setButtonScale(relativeButtonSize)
|
||||
easeButton3!!.setButtonScale(relativeButtonSize)
|
||||
easeButton4!!.setButtonScale(relativeButtonSize)
|
||||
buttonHeightSet = true
|
||||
}
|
||||
mInitialFlipCardHeight = flipCardLayout!!.layoutParams.height
|
||||
if (mLargeAnswerButtons) {
|
||||
initialFlipCardHeight = flipCardLayout!!.layoutParams.height
|
||||
if (largeAnswerButtons) {
|
||||
val params = flipCardLayout!!.layoutParams
|
||||
params.height = mInitialFlipCardHeight * 2
|
||||
params.height = initialFlipCardHeight * 2
|
||||
}
|
||||
answerField = findViewById(R.id.answer_field)
|
||||
initControls()
|
||||
@ -979,14 +979,14 @@ abstract class AbstractFlashcardViewer :
|
||||
getString(R.string.answer_buttons_position_preference),
|
||||
"bottom"
|
||||
)
|
||||
mAnswerButtonsPosition = answerButtonsPosition
|
||||
this.answerButtonsPosition = answerButtonsPosition
|
||||
val answerArea = findViewById<LinearLayout>(R.id.bottom_area_layout)
|
||||
val answerAreaParams = answerArea.layoutParams as RelativeLayout.LayoutParams
|
||||
val whiteboardContainer = findViewById<FrameLayout>(R.id.whiteboard)
|
||||
val whiteboardContainerParams =
|
||||
whiteboardContainer.layoutParams as RelativeLayout.LayoutParams
|
||||
val flashcardContainerParams = mCardFrame!!.layoutParams as RelativeLayout.LayoutParams
|
||||
val touchLayerContainerParams = mTouchLayer!!.layoutParams as RelativeLayout.LayoutParams
|
||||
val flashcardContainerParams = cardFrame!!.layoutParams as RelativeLayout.LayoutParams
|
||||
val touchLayerContainerParams = touchLayer!!.layoutParams as RelativeLayout.LayoutParams
|
||||
when (answerButtonsPosition) {
|
||||
"top" -> {
|
||||
whiteboardContainerParams.addRule(RelativeLayout.BELOW, R.id.bottom_area_layout)
|
||||
@ -1021,8 +1021,8 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
answerArea.layoutParams = answerAreaParams
|
||||
whiteboardContainer.layoutParams = whiteboardContainerParams
|
||||
mCardFrame!!.layoutParams = flashcardContainerParams
|
||||
mTouchLayer!!.layoutParams = touchLayerContainerParams
|
||||
cardFrame!!.layoutParams = flashcardContainerParams
|
||||
touchLayer!!.layoutParams = touchLayerContainerParams
|
||||
}
|
||||
|
||||
protected open fun createWebView(): WebView {
|
||||
@ -1101,13 +1101,13 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
protected fun shouldShowNextReviewTime(): Boolean {
|
||||
return mShowNextReviewTime
|
||||
return showNextReviewTime
|
||||
}
|
||||
|
||||
protected open fun displayAnswerBottomBar() {
|
||||
flipCardLayout!!.isClickable = false
|
||||
easeButtonsLayout!!.visibility = View.VISIBLE
|
||||
if (mLargeAnswerButtons) {
|
||||
if (largeAnswerButtons) {
|
||||
easeButtonsLayout!!.orientation = LinearLayout.VERTICAL
|
||||
easeButtonsLayout!!.removeAllViewsInLayout()
|
||||
easeButton1!!.detachFromParent()
|
||||
@ -1175,12 +1175,12 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
protected open fun switchTopBarVisibility(visible: Int) {
|
||||
mPreviousAnswerIndicator!!.setVisibility(visible)
|
||||
previousAnswerIndicator!!.setVisibility(visible)
|
||||
}
|
||||
|
||||
protected open fun initControls() {
|
||||
mCardFrame!!.visibility = View.VISIBLE
|
||||
mPreviousAnswerIndicator!!.setVisibility(View.VISIBLE)
|
||||
cardFrame!!.visibility = View.VISIBLE
|
||||
previousAnswerIndicator!!.setVisibility(View.VISIBLE)
|
||||
flipCardLayout!!.visibility = View.VISIBLE
|
||||
answerField!!.visibility = if (typeAnswer!!.validForEditText()) View.VISIBLE else View.GONE
|
||||
answerField!!.setOnEditorActionListener { _, actionId: Int, _ ->
|
||||
@ -1207,17 +1207,17 @@ abstract class AbstractFlashcardViewer :
|
||||
// mDeckFilename = preferences.getString("deckFilename", "");
|
||||
minimalClickSpeed = preferences.getInt("showCardAnswerButtonTime", 0)
|
||||
fullscreenMode = fromPreference(preferences)
|
||||
mRelativeButtonSize = preferences.getInt("answerButtonSize", 100)
|
||||
mTTS.enabled = preferences.getBoolean("tts", false)
|
||||
mDoubleScrolling = preferences.getBoolean("double_scrolling", false)
|
||||
relativeButtonSize = preferences.getInt("answerButtonSize", 100)
|
||||
tts.enabled = preferences.getBoolean("tts", false)
|
||||
doubleScrolling = preferences.getBoolean("double_scrolling", false)
|
||||
prefShowTopbar = preferences.getBoolean("showTopbar", true)
|
||||
mLargeAnswerButtons = preferences.getBoolean("showLargeAnswerButtons", false)
|
||||
mDoubleTapTimeInterval =
|
||||
largeAnswerButtons = preferences.getBoolean("showLargeAnswerButtons", false)
|
||||
doubleTapTimeInterval =
|
||||
preferences.getInt(DOUBLE_TAP_TIME_INTERVAL, DEFAULT_DOUBLE_TAP_TIME_INTERVAL)
|
||||
mExitViaDoubleTapBack = preferences.getBoolean("exitViaDoubleTapBack", false)
|
||||
mGesturesEnabled = preferences.getBoolean(GestureProcessor.PREF_KEY, false)
|
||||
if (mGesturesEnabled) {
|
||||
mGestureProcessor.init(preferences)
|
||||
exitViaDoubleTapBack = preferences.getBoolean("exitViaDoubleTapBack", false)
|
||||
gesturesEnabled = preferences.getBoolean(GestureProcessor.PREF_KEY, false)
|
||||
if (gesturesEnabled) {
|
||||
gestureProcessor.init(preferences)
|
||||
}
|
||||
if (preferences.getBoolean("timeoutAnswer", false) || preferences.getBoolean(
|
||||
"keepScreenOn",
|
||||
@ -1232,7 +1232,7 @@ abstract class AbstractFlashcardViewer :
|
||||
protected open fun restoreCollectionPreferences(col: Collection) {
|
||||
// These are preferences we pull out of the collection instead of SharedPreferences
|
||||
try {
|
||||
mShowNextReviewTime = col.config.get("estTimes") ?: true
|
||||
showNextReviewTime = col.config.get("estTimes") ?: true
|
||||
val preferences = baseContext.sharedPrefs()
|
||||
automaticAnswer = AutomaticAnswer.createInstance(this, preferences, col)
|
||||
} catch (ex: Exception) {
|
||||
@ -1252,8 +1252,8 @@ abstract class AbstractFlashcardViewer :
|
||||
if (webView == null) {
|
||||
webView = createWebView()
|
||||
initializeDebugging(this.sharedPrefs())
|
||||
mCardFrame!!.addView(webView)
|
||||
mGestureDetectorImpl.onWebViewCreated(webView!!)
|
||||
cardFrame!!.addView(webView)
|
||||
gestureDetectorImpl.onWebViewCreated(webView!!)
|
||||
}
|
||||
if (webView!!.visibility != View.VISIBLE) {
|
||||
webView!!.visibility = View.VISIBLE
|
||||
@ -1305,7 +1305,7 @@ abstract class AbstractFlashcardViewer :
|
||||
open fun displayCardQuestion() {
|
||||
Timber.d("displayCardQuestion()")
|
||||
displayAnswer = false
|
||||
mBackButtonPressedToReturn = false
|
||||
backButtonPressedToReturn = false
|
||||
setInterface()
|
||||
typeAnswer?.input = ""
|
||||
typeAnswer?.updateInfo(currentCard!!, resources)
|
||||
@ -1316,7 +1316,7 @@ abstract class AbstractFlashcardViewer :
|
||||
} else {
|
||||
answerField?.visibility = View.GONE
|
||||
}
|
||||
val content = mHtmlGenerator!!.generateHtml(currentCard!!, Side.FRONT)
|
||||
val content = htmlGenerator!!.generateHtml(currentCard!!, Side.FRONT)
|
||||
automaticAnswer.onDisplayQuestion()
|
||||
updateCard(content)
|
||||
hideEaseButtons()
|
||||
@ -1333,7 +1333,7 @@ abstract class AbstractFlashcardViewer :
|
||||
actualHideEaseButtons()
|
||||
Timber.d("displayCardAnswer()")
|
||||
mMissingImageHandler.onCardSideChange()
|
||||
mBackButtonPressedToReturn = false
|
||||
backButtonPressedToReturn = false
|
||||
|
||||
// prevent answering (by e.g. gestures) before card is loaded
|
||||
if (currentCard == null) {
|
||||
@ -1356,8 +1356,8 @@ abstract class AbstractFlashcardViewer :
|
||||
if (!typeAnswer!!.useInputTag) {
|
||||
typeAnswer!!.input = answerField!!.text.toString()
|
||||
}
|
||||
mIsSelecting = false
|
||||
val answerContent = mHtmlGenerator!!.generateHtml(currentCard!!, Side.BACK)
|
||||
isSelecting = false
|
||||
val answerContent = htmlGenerator!!.generateHtml(currentCard!!, Side.BACK)
|
||||
automaticAnswer.onDisplayAnswer()
|
||||
updateCard(answerContent)
|
||||
displayAnswerBottomBar()
|
||||
@ -1435,7 +1435,7 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
if (!soundPlayer.config.autoplay && !doAudioReplay) return
|
||||
// Use TTS if TTS preference enabled and no other sound source
|
||||
val useTTS = mTTS.enabled && !soundPlayer.hasSounds(displayAnswer)
|
||||
val useTTS = tts.enabled && !soundPlayer.hasSounds(displayAnswer)
|
||||
// We need to play the sounds from the proper side of the card
|
||||
if (!useTTS) {
|
||||
launchCatchingTask {
|
||||
@ -1450,7 +1450,7 @@ abstract class AbstractFlashcardViewer :
|
||||
val replayQuestion = soundPlayer.config.replayQuestion
|
||||
// Text to speech is in effect here
|
||||
// If the question is displayed or if the question should be replayed, read the question
|
||||
if (mTtsInitialized) {
|
||||
if (ttsInitialized) {
|
||||
if (!displayAnswer || doAudioReplay && replayQuestion) {
|
||||
readCardTts(SingleSoundSide.QUESTION)
|
||||
}
|
||||
@ -1458,14 +1458,14 @@ abstract class AbstractFlashcardViewer :
|
||||
readCardTts(SingleSoundSide.ANSWER)
|
||||
}
|
||||
} else {
|
||||
mReplayOnTtsInit = true
|
||||
replayOnTtsInit = true
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun readCardTts(side: SingleSoundSide) {
|
||||
val tags = legacyGetTtsTags(currentCard!!, side, this)
|
||||
mTTS.readCardText(tags, currentCard!!, side.toSoundSide())
|
||||
tts.readCardText(tags, currentCard!!, side.toSoundSide())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1484,8 +1484,8 @@ abstract class AbstractFlashcardViewer :
|
||||
* Shows the dialogue for selecting TTS for the current card and cardside.
|
||||
*/
|
||||
protected fun showSelectTtsDialogue() {
|
||||
if (mTtsInitialized) {
|
||||
mTTS.selectTts(
|
||||
if (ttsInitialized) {
|
||||
tts.selectTts(
|
||||
this,
|
||||
currentCard!!,
|
||||
if (displayAnswer) SoundSide.ANSWER else SoundSide.QUESTION
|
||||
@ -1500,7 +1500,7 @@ abstract class AbstractFlashcardViewer :
|
||||
return
|
||||
}
|
||||
processCardAction { cardWebView: WebView? -> loadContentIntoCard(cardWebView, cardContent!!) }
|
||||
mGestureDetectorImpl.onFillFlashcard()
|
||||
gestureDetectorImpl.onFillFlashcard()
|
||||
if (!displayAnswer) {
|
||||
updateForNewCard()
|
||||
}
|
||||
@ -1520,17 +1520,17 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
protected open fun unblockControls() {
|
||||
mCardFrame!!.isEnabled = true
|
||||
cardFrame!!.isEnabled = true
|
||||
flipCardLayout?.isEnabled = true
|
||||
easeButton1?.unblockBasedOnEase(mCurrentEase)
|
||||
easeButton2?.unblockBasedOnEase(mCurrentEase)
|
||||
easeButton3?.unblockBasedOnEase(mCurrentEase)
|
||||
easeButton4?.unblockBasedOnEase(mCurrentEase)
|
||||
easeButton1?.unblockBasedOnEase(currentEase)
|
||||
easeButton2?.unblockBasedOnEase(currentEase)
|
||||
easeButton3?.unblockBasedOnEase(currentEase)
|
||||
easeButton4?.unblockBasedOnEase(currentEase)
|
||||
if (typeAnswer?.validForEditText() == true) {
|
||||
answerField?.isEnabled = true
|
||||
}
|
||||
mTouchLayer?.visibility = View.VISIBLE
|
||||
mInAnswer = false
|
||||
touchLayer?.visibility = View.VISIBLE
|
||||
inAnswer = false
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
@ -1751,7 +1751,7 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
override val baseSnackbarBuilder: SnackbarBuilder = {
|
||||
// Configure the snackbar to avoid the bottom answer buttons
|
||||
if (mAnswerButtonsPosition == "bottom") {
|
||||
if (answerButtonsPosition == "bottom") {
|
||||
val easeButtons = findViewById<View>(R.id.answer_options_layout)
|
||||
val previewButtons = findViewById<View>(R.id.preview_buttons_layout)
|
||||
anchorView = if (previewButtons.isVisible) previewButtons else easeButtons
|
||||
@ -1804,8 +1804,8 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
protected open fun closeReviewer(result: Int) {
|
||||
automaticAnswer.disable()
|
||||
mPreviousAnswerIndicator!!.stopAutomaticHide()
|
||||
mLongClickHandler.removeCallbacks(mStartLongClickAction)
|
||||
previousAnswerIndicator!!.stopAutomaticHide()
|
||||
longClickHandler.removeCallbacks(startLongClickAction)
|
||||
this@AbstractFlashcardViewer.setResult(result)
|
||||
finish()
|
||||
}
|
||||
@ -1827,7 +1827,7 @@ abstract class AbstractFlashcardViewer :
|
||||
if (!isDisplayingAnswer) {
|
||||
Timber.d("displayCardQuestion()")
|
||||
displayAnswer = false
|
||||
mBackButtonPressedToReturn = false
|
||||
backButtonPressedToReturn = false
|
||||
setInterface()
|
||||
typeAnswer?.input = ""
|
||||
typeAnswer?.updateInfo(currentCard!!, resources)
|
||||
@ -1838,7 +1838,7 @@ abstract class AbstractFlashcardViewer :
|
||||
} else {
|
||||
answerField?.visibility = View.GONE
|
||||
}
|
||||
val content = mHtmlGenerator!!.generateHtml(currentCard!!, Side.FRONT)
|
||||
val content = htmlGenerator!!.generateHtml(currentCard!!, Side.FRONT)
|
||||
automaticAnswer.onDisplayQuestion()
|
||||
updateCard(content)
|
||||
hideEaseButtons()
|
||||
@ -1870,13 +1870,13 @@ abstract class AbstractFlashcardViewer :
|
||||
override fun onScrollChanged(horiz: Int, vert: Int, oldHoriz: Int, oldVert: Int) {
|
||||
super.onScrollChanged(horiz, vert, oldHoriz, oldVert)
|
||||
if (abs(horiz - oldHoriz) > abs(vert - oldVert)) {
|
||||
mIsXScrolling = true
|
||||
mScrollHandler.removeCallbacks(mScrollXRunnable)
|
||||
mScrollHandler.postDelayed(mScrollXRunnable, 300)
|
||||
isXScrolling = true
|
||||
scrollHandler.removeCallbacks(scrollXRunnable)
|
||||
scrollHandler.postDelayed(scrollXRunnable, 300)
|
||||
} else {
|
||||
mIsYScrolling = true
|
||||
mScrollHandler.removeCallbacks(mScrollYRunnable)
|
||||
mScrollHandler.postDelayed(mScrollYRunnable, 300)
|
||||
isYScrolling = true
|
||||
scrollHandler.removeCallbacks(scrollYRunnable)
|
||||
scrollHandler.postDelayed(scrollYRunnable, 300)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1911,9 +1911,9 @@ abstract class AbstractFlashcardViewer :
|
||||
return null
|
||||
}
|
||||
|
||||
private val mScrollHandler = newHandler()
|
||||
private val mScrollXRunnable = Runnable { mIsXScrolling = false }
|
||||
private val mScrollYRunnable = Runnable { mIsYScrolling = false }
|
||||
private val scrollHandler = newHandler()
|
||||
private val scrollXRunnable = Runnable { isXScrolling = false }
|
||||
private val scrollYRunnable = Runnable { isYScrolling = false }
|
||||
}
|
||||
|
||||
internal open inner class MyGestureDetector : SimpleOnGestureListener() {
|
||||
@ -1935,18 +1935,18 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
// Go back to immersive mode if the user had temporarily exited it (and then execute swipe gesture)
|
||||
this@AbstractFlashcardViewer.onFling()
|
||||
if (e1 != null && mGesturesEnabled) {
|
||||
if (e1 != null && gesturesEnabled) {
|
||||
try {
|
||||
val dy = e2.y - e1.y
|
||||
val dx = e2.x - e1.x
|
||||
mGestureProcessor.onFling(
|
||||
gestureProcessor.onFling(
|
||||
dx,
|
||||
dy,
|
||||
velocityX,
|
||||
velocityY,
|
||||
mIsSelecting,
|
||||
mIsXScrolling,
|
||||
mIsYScrolling
|
||||
isSelecting,
|
||||
isXScrolling,
|
||||
isYScrolling
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "onFling Exception")
|
||||
@ -1956,15 +1956,15 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
private fun isTouchingEdge(e1: MotionEvent): Boolean {
|
||||
val height = mTouchLayer!!.height
|
||||
val width = mTouchLayer!!.width
|
||||
val height = touchLayer!!.height
|
||||
val width = touchLayer!!.width
|
||||
val margin = Companion.NO_GESTURE_BORDER_DIP * resources.displayMetrics.density + 0.5f
|
||||
return e1.x < margin || e1.y < margin || height - e1.y < margin || width - e1.x < margin
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
if (mGesturesEnabled) {
|
||||
mGestureProcessor.onDoubleTap()
|
||||
if (gesturesEnabled) {
|
||||
gestureProcessor.onDoubleTap()
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -1983,14 +1983,14 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
protected open fun executeTouchCommand(e: MotionEvent) {
|
||||
if (mGesturesEnabled && !mIsSelecting) {
|
||||
val height = mTouchLayer!!.height
|
||||
val width = mTouchLayer!!.width
|
||||
if (gesturesEnabled && !isSelecting) {
|
||||
val height = touchLayer!!.height
|
||||
val width = touchLayer!!.width
|
||||
val posX = e.x
|
||||
val posY = e.y
|
||||
mGestureProcessor.onTap(height, width, posX, posY)
|
||||
gestureProcessor.onTap(height, width, posX, posY)
|
||||
}
|
||||
mIsSelecting = false
|
||||
isSelecting = false
|
||||
}
|
||||
|
||||
open fun onWebViewCreated(webView: WebView) {
|
||||
@ -2032,7 +2032,7 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
private fun initShakeDetector() {
|
||||
Timber.d("Initializing shake detector")
|
||||
if (mGestureProcessor.isBound(Gesture.SHAKE)) {
|
||||
if (gestureProcessor.isBound(Gesture.SHAKE)) {
|
||||
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
shakeDetector = ShakeDetector(this).apply {
|
||||
start(sensorManager, SensorManager.SENSOR_DELAY_UI)
|
||||
@ -2052,25 +2052,25 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
/** A list of events to process when listening to WebView touches */
|
||||
private val mDesiredTouchEvents = hashSetInit<MotionEvent>(2)
|
||||
private val desiredTouchEvents = hashSetInit<MotionEvent>(2)
|
||||
|
||||
/** A list of events we sent to the WebView (to block double-processing) */
|
||||
private val mDispatchedTouchEvents = hashSetInit<MotionEvent>(2)
|
||||
private val dispatchedTouchEvents = hashSetInit<MotionEvent>(2)
|
||||
|
||||
override fun hearShake() {
|
||||
Timber.d("Shake detected!")
|
||||
mGestureProcessor.onShake()
|
||||
gestureProcessor.onShake()
|
||||
}
|
||||
|
||||
override fun onFillFlashcard() {
|
||||
Timber.d("Removing pending touch events for gestures")
|
||||
mDesiredTouchEvents.clear()
|
||||
mDispatchedTouchEvents.clear()
|
||||
desiredTouchEvents.clear()
|
||||
dispatchedTouchEvents.clear()
|
||||
}
|
||||
|
||||
override fun eventCanBeSentToWebView(event: MotionEvent): Boolean {
|
||||
// if we processed the event, we don't want to perform it again
|
||||
return !mDispatchedTouchEvents.remove(event)
|
||||
return !dispatchedTouchEvents.remove(event)
|
||||
}
|
||||
|
||||
override fun executeTouchCommand(e: MotionEvent) {
|
||||
@ -2079,12 +2079,12 @@ abstract class AbstractFlashcardViewer :
|
||||
upEvent.action = MotionEvent.ACTION_UP
|
||||
|
||||
// mark the events we want to process
|
||||
mDesiredTouchEvents.add(e)
|
||||
mDesiredTouchEvents.add(upEvent)
|
||||
desiredTouchEvents.add(e)
|
||||
desiredTouchEvents.add(upEvent)
|
||||
|
||||
// mark the events to can guard against double-processing
|
||||
mDispatchedTouchEvents.add(e)
|
||||
mDispatchedTouchEvents.add(upEvent)
|
||||
dispatchedTouchEvents.add(e)
|
||||
dispatchedTouchEvents.add(upEvent)
|
||||
Timber.d("Dispatching touch events")
|
||||
processCardAction { cardWebView: WebView? ->
|
||||
cardWebView!!.dispatchTouchEvent(e)
|
||||
@ -2096,7 +2096,7 @@ abstract class AbstractFlashcardViewer :
|
||||
override fun onWebViewCreated(webView: WebView) {
|
||||
Timber.d("Initializing WebView touch handler")
|
||||
webView.setOnTouchListener { webViewAsView: View, motionEvent: MotionEvent ->
|
||||
if (!mDesiredTouchEvents.remove(motionEvent)) {
|
||||
if (!desiredTouchEvents.remove(motionEvent)) {
|
||||
return@setOnTouchListener false
|
||||
}
|
||||
|
||||
@ -2136,8 +2136,8 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
/** Callback for when TTS has been initialized. */
|
||||
fun ttsInitialized() {
|
||||
mTtsInitialized = true
|
||||
if (mReplayOnTtsInit) {
|
||||
ttsInitialized = true
|
||||
if (replayOnTtsInit) {
|
||||
playSounds(true)
|
||||
}
|
||||
}
|
||||
@ -2147,7 +2147,7 @@ abstract class AbstractFlashcardViewer :
|
||||
}
|
||||
|
||||
val writeLock: Lock
|
||||
get() = mCardLock.writeLock()
|
||||
get() = cardLock.writeLock()
|
||||
open var currentCard: Card? = null
|
||||
|
||||
/** Refreshes the WebView after a crash */
|
||||
@ -2155,13 +2155,13 @@ abstract class AbstractFlashcardViewer :
|
||||
// Destroy the current WebView (to ensure WebView is GCed).
|
||||
// Otherwise, we get the following error:
|
||||
// "crash wasn't handled by all associated webviews, triggering application crash"
|
||||
mCardFrame!!.removeAllViews()
|
||||
mCardFrameParent!!.removeView(mCardFrame)
|
||||
cardFrame!!.removeAllViews()
|
||||
cardFrameParent!!.removeView(cardFrame)
|
||||
// destroy after removal from the view - produces logcat warnings otherwise
|
||||
destroyWebView(webView)
|
||||
webView = null
|
||||
// inflate a new instance of mCardFrame
|
||||
mCardFrame = inflateNewView<FrameLayout>(R.id.flashcard)
|
||||
cardFrame = inflateNewView<FrameLayout>(R.id.flashcard)
|
||||
// Even with the above, I occasionally saw the above error. Manually trigger the GC.
|
||||
// I'll keep this line unless I see another crash, which would point to another underlying issue.
|
||||
System.gc()
|
||||
@ -2169,7 +2169,7 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
fun recreateWebViewFrame() {
|
||||
// we need to add at index 0 so gestures still go through.
|
||||
mCardFrameParent!!.addView(mCardFrame, 0)
|
||||
cardFrameParent!!.addView(cardFrame, 0)
|
||||
recreateWebView()
|
||||
}
|
||||
|
||||
@ -2459,7 +2459,7 @@ abstract class AbstractFlashcardViewer :
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
|
||||
return mOnRenderProcessGoneDelegate.onRenderProcessGone(view, detail)
|
||||
return onRenderProcessGoneDelegate.onRenderProcessGone(view, detail)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2514,7 +2514,7 @@ abstract class AbstractFlashcardViewer :
|
||||
internal fun showTagsDialog() {
|
||||
val tags = ArrayList(getColUnsafe.tags.all())
|
||||
val selTags = ArrayList(currentCard!!.note().tags)
|
||||
val dialog = mTagsDialogFactory!!.newTagsDialog()
|
||||
val dialog = tagsDialogFactory!!.newTagsDialog()
|
||||
.withArguments(TagsDialog.DialogType.EDIT_TAGS, selTags, tags)
|
||||
showDialogFragment(dialog)
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ open class AnkiDroidJsAPI(private val activity: AbstractFlashcardViewer) {
|
||||
private val context: Context = activity
|
||||
|
||||
// Text to speech
|
||||
private val mTalker = JavaScriptTTS()
|
||||
private val talker = JavaScriptTTS()
|
||||
|
||||
open fun convertToByteArray(apiContract: ApiContract, boolean: Boolean): ByteArray {
|
||||
return ApiResult(apiContract.isValid, boolean.toString()).toString().toByteArray()
|
||||
@ -244,22 +244,22 @@ open class AnkiDroidJsAPI(private val activity: AbstractFlashcardViewer) {
|
||||
"cardDue" -> convertToByteArray(apiContract, currentCard.due)
|
||||
"deckName" -> convertToByteArray(apiContract, Decks.basename(activity.getColUnsafe.decks.name(currentCard.did)))
|
||||
"isActiveNetworkMetered" -> convertToByteArray(apiContract, NetworkUtils.isActiveNetworkMetered())
|
||||
"ttsSetLanguage" -> convertToByteArray(apiContract, mTalker.setLanguage(apiParams))
|
||||
"ttsSetLanguage" -> convertToByteArray(apiContract, talker.setLanguage(apiParams))
|
||||
"ttsSpeak" -> {
|
||||
val jsonObject = JSONObject(apiParams)
|
||||
val text = jsonObject.getString("text")
|
||||
val queueMode = jsonObject.getInt("queueMode")
|
||||
convertToByteArray(apiContract, mTalker.speak(text, queueMode))
|
||||
convertToByteArray(apiContract, talker.speak(text, queueMode))
|
||||
}
|
||||
"ttsIsSpeaking" -> convertToByteArray(apiContract, mTalker.isSpeaking)
|
||||
"ttsSetPitch" -> convertToByteArray(apiContract, mTalker.setPitch(apiParams.toFloat()))
|
||||
"ttsSetSpeechRate" -> convertToByteArray(apiContract, mTalker.setSpeechRate(apiParams.toFloat()))
|
||||
"ttsIsSpeaking" -> convertToByteArray(apiContract, talker.isSpeaking)
|
||||
"ttsSetPitch" -> convertToByteArray(apiContract, talker.setPitch(apiParams.toFloat()))
|
||||
"ttsSetSpeechRate" -> convertToByteArray(apiContract, talker.setSpeechRate(apiParams.toFloat()))
|
||||
"ttsFieldModifierIsAvailable" -> {
|
||||
// Know if {{tts}} is supported - issue #10443
|
||||
// Return false for now
|
||||
convertToByteArray(apiContract, false)
|
||||
}
|
||||
"ttsStop" -> convertToByteArray(apiContract, mTalker.stop())
|
||||
"ttsStop" -> convertToByteArray(apiContract, talker.stop())
|
||||
"searchCard" -> {
|
||||
val intent = Intent(context, CardBrowser::class.java).apply {
|
||||
putExtra("currentCard", currentCard.id)
|
||||
|
@ -1156,7 +1156,7 @@ open class CardBrowser :
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun exportDialogsFactory(): ExportDialogsFactory = mExportingDelegate.mDialogsFactory
|
||||
override fun exportDialogsFactory(): ExportDialogsFactory = mExportingDelegate.dialogsFactory
|
||||
|
||||
private fun exportSelected() = launchCatchingTask {
|
||||
val (type, selectedIds) = viewModel.getSelectionExportData() ?: return@launchCatchingTask
|
||||
|
@ -24,6 +24,7 @@ import com.ichi2.compat.CompatHelper.Companion.compat
|
||||
import com.ichi2.compat.CompatHelper.Companion.getSerializableCompat
|
||||
import com.ichi2.libanki.NoteTypeId
|
||||
import com.ichi2.libanki.NotetypeJson
|
||||
import com.ichi2.utils.KotlinCleanup
|
||||
import org.json.JSONObject
|
||||
import timber.log.Timber
|
||||
import java.io.ByteArrayInputStream
|
||||
@ -32,22 +33,23 @@ import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
/** A wrapper for a notetype in JSON format with helpers for editing the notetype. */
|
||||
@KotlinCleanup("_templateChanges -> use templateChanges")
|
||||
class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
enum class ChangeType {
|
||||
ADD, DELETE
|
||||
}
|
||||
|
||||
private var mTemplateChanges = ArrayList<Array<Any>>()
|
||||
private var _templateChanges = ArrayList<Array<Any>>()
|
||||
var editedModelFileName: String? = null
|
||||
|
||||
fun toBundle(): Bundle = bundleOf(
|
||||
INTENT_MODEL_FILENAME to saveTempModel(AnkiDroidApp.instance.applicationContext, notetype),
|
||||
"mTemplateChanges" to mTemplateChanges
|
||||
"mTemplateChanges" to _templateChanges
|
||||
)
|
||||
|
||||
private fun loadTemplateChanges(bundle: Bundle) {
|
||||
try {
|
||||
mTemplateChanges = bundle.getSerializableCompat("mTemplateChanges")!!
|
||||
_templateChanges = bundle.getSerializableCompat("mTemplateChanges")!!
|
||||
} catch (e: ClassCastException) {
|
||||
Timber.e(e, "Unexpected cast failure")
|
||||
}
|
||||
@ -137,17 +139,17 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
Timber.d("getDeleteDbOrds()")
|
||||
|
||||
// array containing the original / db-relative ordinals for all pending deletes plus the proposed one
|
||||
val deletedDbOrds = ArrayList<Int>(mTemplateChanges.size)
|
||||
val deletedDbOrds = ArrayList<Int>(_templateChanges.size)
|
||||
|
||||
// For each entry in the changes list - and the proposed delete - scan for deletes to get original ordinal
|
||||
for (i in 0..mTemplateChanges.size) {
|
||||
for (i in 0.._templateChanges.size) {
|
||||
var ordinalAdjustment = 0
|
||||
|
||||
// We need an initializer. Though proposed change is checked last, it's a reasonable default initializer.
|
||||
var currentChange = arrayOf<Any>(ord, ChangeType.DELETE)
|
||||
if (i < mTemplateChanges.size) {
|
||||
if (i < _templateChanges.size) {
|
||||
// Until we exhaust the pending change list we will use them
|
||||
currentChange = mTemplateChanges[i]
|
||||
currentChange = _templateChanges[i]
|
||||
}
|
||||
|
||||
// If the current pending change isn't a delete, it is unimportant here
|
||||
@ -157,7 +159,7 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
|
||||
// If it is a delete, scan previous deletes and shift as necessary for original ord
|
||||
for (j in 0 until i) {
|
||||
val previousChange = mTemplateChanges[j]
|
||||
val previousChange = _templateChanges[j]
|
||||
|
||||
// Is previous change a delete? Lower ordinal than current change?
|
||||
if (previousChange[1] === ChangeType.DELETE && previousChange[0] as Int <= currentChange[0] as Int) {
|
||||
@ -182,8 +184,8 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
return
|
||||
}
|
||||
val adjustedChanges = adjustedTemplateChanges
|
||||
for (i in mTemplateChanges.indices) {
|
||||
val change = mTemplateChanges[i]
|
||||
for (i in _templateChanges.indices) {
|
||||
val change = _templateChanges[i]
|
||||
val adjustedChange = adjustedChanges[i]
|
||||
Timber.d("dumpChanges() Change %s is ord/type %s/%s", i, change[0], change[1])
|
||||
Timber.d(
|
||||
@ -197,7 +199,7 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
|
||||
val templateChanges: ArrayList<Array<Any>>
|
||||
get() {
|
||||
return mTemplateChanges
|
||||
return _templateChanges
|
||||
}
|
||||
|
||||
/**
|
||||
@ -247,8 +249,8 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
var postChange = false
|
||||
var ordinalAdjustment = 0
|
||||
var i = 0
|
||||
while (i < mTemplateChanges.size) {
|
||||
val change = mTemplateChanges[i]
|
||||
while (i < _templateChanges.size) {
|
||||
val change = _templateChanges[i]
|
||||
var ordinal = change[0] as Int
|
||||
val changeType = change[1] as ChangeType
|
||||
Timber.d("compactTemplateChanges() examining change entry %s / %s", ordinal, changeType)
|
||||
@ -259,7 +261,7 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
Timber.d("compactTemplateChanges() found our entry at index %s", i)
|
||||
// Remove this entry to start compaction, then fix up the loop counter since we altered size
|
||||
postChange = true
|
||||
mTemplateChanges.removeAt(i)
|
||||
_templateChanges.removeAt(i)
|
||||
i--
|
||||
}
|
||||
i++
|
||||
|
@ -40,19 +40,19 @@ import java.io.IOException
|
||||
*/
|
||||
@NeedsTest("after switch to new schema as default, add test to confirm audio tags rendered")
|
||||
open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
private var mEditedModelFileName: String? = null
|
||||
private var mEditedNotetype: NotetypeJson? = null
|
||||
private var mOrdinal = 0
|
||||
private var editedModelFileName: String? = null
|
||||
private var editedNotetype: NotetypeJson? = null
|
||||
private var ordinal = 0
|
||||
|
||||
/** The index of the card in cardList to show */
|
||||
private var mCardListIndex = 0
|
||||
private var cardListIndex = 0
|
||||
|
||||
/** The list (currently singular) of cards to be previewed
|
||||
* A single template was selected, and there was an associated card which exists
|
||||
*/
|
||||
private var mCardList: LongArray? = null
|
||||
private var mNoteEditorBundle: Bundle? = null
|
||||
private var mShowingAnswer = false
|
||||
private var cardList: LongArray? = null
|
||||
private var noteEditorBundle: Bundle? = null
|
||||
private var showingAnswer = false
|
||||
|
||||
/**
|
||||
* The number of valid cards for the note
|
||||
@ -68,8 +68,8 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
*/
|
||||
var cardIndex = 0
|
||||
private set
|
||||
private var mAllFieldsNull = true
|
||||
private var mCardType: String? = null
|
||||
private var allFieldsNull = true
|
||||
private var cardType: String? = null
|
||||
protected var previewLayout: PreviewLayout? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (showedActivityFailedScreen(savedInstanceState)) {
|
||||
@ -81,20 +81,20 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
parameters = intent.extras
|
||||
}
|
||||
if (parameters != null) {
|
||||
mNoteEditorBundle = parameters.getBundle("noteEditorBundle")
|
||||
mEditedModelFileName = parameters.getString(CardTemplateNotetype.INTENT_MODEL_FILENAME)
|
||||
mCardList = parameters.getLongArray("cardList")
|
||||
mOrdinal = parameters.getInt("ordinal")
|
||||
mCardListIndex = parameters.getInt("cardListIndex")
|
||||
mShowingAnswer = parameters.getBoolean("showingAnswer", mShowingAnswer)
|
||||
noteEditorBundle = parameters.getBundle("noteEditorBundle")
|
||||
editedModelFileName = parameters.getString(CardTemplateNotetype.INTENT_MODEL_FILENAME)
|
||||
cardList = parameters.getLongArray("cardList")
|
||||
ordinal = parameters.getInt("ordinal")
|
||||
cardListIndex = parameters.getInt("cardListIndex")
|
||||
showingAnswer = parameters.getBoolean("showingAnswer", showingAnswer)
|
||||
}
|
||||
if (mEditedModelFileName != null) {
|
||||
Timber.d("onCreate() loading edited model from %s", mEditedModelFileName)
|
||||
if (editedModelFileName != null) {
|
||||
Timber.d("onCreate() loading edited model from %s", editedModelFileName)
|
||||
try {
|
||||
mEditedNotetype = CardTemplateNotetype.getTempModel(mEditedModelFileName!!)
|
||||
mCardType = mEditedNotetype!!.optString("name")
|
||||
editedNotetype = CardTemplateNotetype.getTempModel(editedModelFileName!!)
|
||||
cardType = editedNotetype!!.optString("name")
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e, "Unable to load temp model from file %s", mEditedModelFileName)
|
||||
Timber.w(e, "Unable to load temp model from file %s", editedModelFileName)
|
||||
closeCardTemplatePreviewer()
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,7 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (currentCard == null || mOrdinal < 0) {
|
||||
if (currentCard == null || ordinal < 0) {
|
||||
Timber.e("CardTemplatePreviewer started with empty card list or invalid index")
|
||||
closeCardTemplatePreviewer()
|
||||
}
|
||||
@ -142,7 +142,7 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
topBarLayout!!.visibility = View.GONE
|
||||
findViewById<View>(R.id.answer_options_layout).visibility = View.GONE
|
||||
findViewById<View>(R.id.bottom_area_layout).visibility = View.VISIBLE
|
||||
previewLayout = createAndDisplay(this, mToggleAnswerHandler)
|
||||
previewLayout = createAndDisplay(this, toggleAnswerHandler)
|
||||
previewLayout!!.setOnPreviousCard { onPreviousCard() }
|
||||
previewLayout!!.setOnNextCard { onNextCard() }
|
||||
previewLayout!!.hideNavigationButtons()
|
||||
@ -151,16 +151,16 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
|
||||
override fun displayCardQuestion() {
|
||||
super.displayCardQuestion()
|
||||
mShowingAnswer = false
|
||||
showingAnswer = false
|
||||
previewLayout!!.setShowingAnswer(false)
|
||||
}
|
||||
|
||||
override fun displayCardAnswer() {
|
||||
if (mAllFieldsNull && mCardType != null && mCardType == getString(R.string.basic_typing_model_name)) {
|
||||
if (allFieldsNull && cardType != null && cardType == getString(R.string.basic_typing_model_name)) {
|
||||
answerField!!.setText(getString(R.string.basic_answer_sample_text_user))
|
||||
}
|
||||
super.displayCardAnswer()
|
||||
mShowingAnswer = true
|
||||
showingAnswer = true
|
||||
previewLayout!!.setShowingAnswer(true)
|
||||
}
|
||||
|
||||
@ -172,8 +172,8 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
private val mToggleAnswerHandler = View.OnClickListener {
|
||||
if (mShowingAnswer) {
|
||||
private val toggleAnswerHandler = View.OnClickListener {
|
||||
if (showingAnswer) {
|
||||
displayCardQuestion()
|
||||
} else {
|
||||
displayCardAnswer()
|
||||
@ -221,21 +221,21 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
public override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putString(CardTemplateNotetype.INTENT_MODEL_FILENAME, mEditedModelFileName)
|
||||
outState.putLongArray("cardList", mCardList)
|
||||
outState.putInt("ordinal", mOrdinal)
|
||||
outState.putInt("cardListIndex", mCardListIndex)
|
||||
outState.putBundle("noteEditorBundle", mNoteEditorBundle)
|
||||
outState.putBoolean("showingAnswer", mShowingAnswer)
|
||||
outState.putString(CardTemplateNotetype.INTENT_MODEL_FILENAME, editedModelFileName)
|
||||
outState.putLongArray("cardList", cardList)
|
||||
outState.putInt("ordinal", ordinal)
|
||||
outState.putInt("cardListIndex", cardListIndex)
|
||||
outState.putBundle("noteEditorBundle", noteEditorBundle)
|
||||
outState.putBoolean("showingAnswer", showingAnswer)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onCollectionLoaded(col: Collection) {
|
||||
super.onCollectionLoaded(col)
|
||||
if (mNoteEditorBundle != null) {
|
||||
mAllFieldsNull = false
|
||||
cardIndex = indexFromOrdinal(col, mNoteEditorBundle!!, mOrdinal)
|
||||
Timber.d("ord %d => idx %d", mOrdinal, cardIndex)
|
||||
if (noteEditorBundle != null) {
|
||||
allFieldsNull = false
|
||||
cardIndex = indexFromOrdinal(col, noteEditorBundle!!, ordinal)
|
||||
Timber.d("ord %d => idx %d", ordinal, cardIndex)
|
||||
// loading from the note editor
|
||||
val toPreview = setCurrentCardFromNoteEditorBundle(col)
|
||||
if (toPreview != null) {
|
||||
@ -246,13 +246,13 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
} else {
|
||||
// loading from the card template editor
|
||||
mAllFieldsNull = true
|
||||
allFieldsNull = true
|
||||
// card template with associated card due to opening from note editor
|
||||
if (mCardList != null && mCardListIndex >= 0 && mCardListIndex < mCardList!!.size) {
|
||||
currentCard = PreviewerCard(col, mCardList!![mCardListIndex])
|
||||
} else if (mEditedNotetype != null) { // bare note type (not coming from note editor), or new card template
|
||||
if (cardList != null && cardListIndex >= 0 && cardListIndex < cardList!!.size) {
|
||||
currentCard = PreviewerCard(col, cardList!![cardListIndex])
|
||||
} else if (editedNotetype != null) { // bare note type (not coming from note editor), or new card template
|
||||
Timber.d("onCreate() CardTemplatePreviewer started with edited model and template index, displaying blank to preview formatting")
|
||||
currentCard = getDummyCard(mEditedNotetype!!, mOrdinal)
|
||||
currentCard = getDummyCard(editedNotetype!!, ordinal)
|
||||
if (currentCard == null) {
|
||||
showThemedToast(applicationContext, getString(R.string.invalid_template), false)
|
||||
closeCardTemplatePreviewer()
|
||||
@ -265,7 +265,7 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
return
|
||||
}
|
||||
displayCardQuestion()
|
||||
if (mShowingAnswer) {
|
||||
if (showingAnswer) {
|
||||
displayCardAnswer()
|
||||
}
|
||||
showBackIcon()
|
||||
@ -276,19 +276,19 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
private fun setCurrentCardFromNoteEditorBundle(col: Collection): Card? {
|
||||
assert(mNoteEditorBundle != null)
|
||||
currentCard = getDummyCard(mEditedNotetype, cardIndex, getBundleEditFields(mNoteEditorBundle))
|
||||
assert(noteEditorBundle != null)
|
||||
currentCard = getDummyCard(editedNotetype, cardIndex, getBundleEditFields(noteEditorBundle))
|
||||
// example: a basic card with no fields provided
|
||||
if (currentCard == null) {
|
||||
return null
|
||||
}
|
||||
val newDid = mNoteEditorBundle!!.getLong("did")
|
||||
val newDid = noteEditorBundle!!.getLong("did")
|
||||
if (col.decks.isDyn(newDid)) {
|
||||
currentCard!!.oDid = currentCard!!.did
|
||||
}
|
||||
currentCard!!.did = newDid
|
||||
val currentNote = currentCard!!.note()
|
||||
val tagsList = mNoteEditorBundle!!.getStringArrayList("tags")
|
||||
val tagsList = noteEditorBundle!!.getStringArrayList("tags")
|
||||
setTags(currentNote, tagsList)
|
||||
return currentCard
|
||||
}
|
||||
@ -310,10 +310,10 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
private fun getLabels(fieldValues: MutableList<String>) {
|
||||
if (mCardType != null && mCardType == getString(R.string.cloze_model_name)) {
|
||||
if (cardType != null && cardType == getString(R.string.cloze_model_name)) {
|
||||
fieldValues[0] = getString(R.string.cloze_sample_text, "c1")
|
||||
}
|
||||
if (mCardType != null && mCardType == getString(R.string.basic_typing_model_name)) {
|
||||
if (cardType != null && cardType == getString(R.string.basic_typing_model_name)) {
|
||||
fieldValues[1] = getString(R.string.basic_answer_sample_text)
|
||||
}
|
||||
}
|
||||
@ -332,14 +332,14 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
private fun indexFromOrdinal(col: Collection, fieldsBundle: Bundle, ordinal: Int): Int {
|
||||
return when (mEditedNotetype?.isCloze) {
|
||||
return when (editedNotetype?.isCloze) {
|
||||
true -> {
|
||||
val note = col.newNote(mEditedNotetype!!).apply {
|
||||
val note = col.newNote(editedNotetype!!).apply {
|
||||
for ((index, field) in getBundleEditFields(fieldsBundle).withIndex()) {
|
||||
this.setField(index, field)
|
||||
}
|
||||
}
|
||||
val clozeNumber = mOrdinal + 1
|
||||
val clozeNumber = this.ordinal + 1
|
||||
col.clozeNumbersInNote(note).indexOf(clozeNumber)
|
||||
}
|
||||
else -> ordinal
|
||||
@ -363,15 +363,15 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
if (notetype == null) {
|
||||
return null
|
||||
}
|
||||
if (mAllFieldsNull) {
|
||||
if (allFieldsNull) {
|
||||
getLabels(fieldValues)
|
||||
}
|
||||
val n = getColUnsafe.newNote(notetype)
|
||||
var i = 0
|
||||
while (i < fieldValues.size && i < n.fields.size) {
|
||||
if (mAllFieldsNull) {
|
||||
if (mCardType != null && mCardType == getString(R.string.cloze_model_name) && i == 0 ||
|
||||
mCardType == getString(R.string.basic_typing_model_name) && i == 1
|
||||
if (allFieldsNull) {
|
||||
if (cardType != null && cardType == getString(R.string.cloze_model_name) && i == 0 ||
|
||||
cardType == getString(R.string.basic_typing_model_name) && i == 1
|
||||
) {
|
||||
n.setField(i, fieldValues[i])
|
||||
} else {
|
||||
@ -393,27 +393,27 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
|
||||
|
||||
/** Override certain aspects of Card behavior so we may display unsaved data */
|
||||
inner class PreviewerCard(col: Collection, id: Long) : Card(col, id) {
|
||||
private val mNote: Note? = null
|
||||
private val _note: Note? = null
|
||||
|
||||
/* if we have an unsaved note saved, use it instead of a collection lookup */
|
||||
override fun note(
|
||||
reload: Boolean
|
||||
): Note {
|
||||
return mNote ?: super.note(reload)
|
||||
return _note ?: super.note(reload)
|
||||
}
|
||||
|
||||
/** if we have an unsaved note saved, use it instead of a collection lookup */
|
||||
override fun note(): Note {
|
||||
return mNote ?: super.note()
|
||||
return _note ?: super.note()
|
||||
}
|
||||
|
||||
/** if we have an unsaved note, never return empty */
|
||||
val isEmpty: Boolean
|
||||
get() = mNote != null
|
||||
get() = _note != null
|
||||
|
||||
/** Override the method that fetches the model so we can render unsaved models */
|
||||
override fun model(): NotetypeJson {
|
||||
return mEditedNotetype ?: super.model()
|
||||
return editedNotetype ?: super.model()
|
||||
}
|
||||
|
||||
override fun renderOutput(reload: Boolean, browser: Boolean): TemplateRenderOutput {
|
||||
|
@ -151,19 +151,19 @@ const val OLDEST_WORKING_WEBVIEW_VERSION = 77
|
||||
* Responsibilities:
|
||||
* * Setup/upgrades of the application: [handleStartup]
|
||||
* * Error handling [handleDbError] [handleDbLocked]
|
||||
* * Displaying a tree of decks, some of which may be collapsible: [mDeckListAdapter]
|
||||
* * Displaying a tree of decks, some of which may be collapsible: [deckListAdapter]
|
||||
* * Allows users to study the decks
|
||||
* * Displays deck progress
|
||||
* * A long press opens a menu allowing modification of the deck
|
||||
* * Filtering decks (if more than 10) [mToolbarSearchView]
|
||||
* * Filtering decks (if more than 10) [toolbarSearchView]
|
||||
* * Controlling syncs
|
||||
* * A user may [pull down][mPullToSyncWrapper] on the 'tree view' to sync
|
||||
* * A user may [pull down][pullToSyncWrapper] on the 'tree view' to sync
|
||||
* * A [button][updateSyncIconFromState] which relies on [SyncStatus] to display whether a sync is needed
|
||||
* * Blocks the UI and displays sync progress when syncing
|
||||
* * Displaying 'General' AnkiDroid options: backups, import, 'check media' etc...
|
||||
* * General handler for error/global dialogs (search for 'as DeckPicker')
|
||||
* * Such as import: [ImportDialogListener]
|
||||
* * A Floating Action Button [mFloatingActionMenu] allowing the user to quickly add notes/cards.
|
||||
* * A Floating Action Button [floatingActionMenu] allowing the user to quickly add notes/cards.
|
||||
* * A custom image as a background can be added: [applyDeckPickerBackground]
|
||||
*/
|
||||
@KotlinCleanup("lots to do")
|
||||
@ -187,42 +187,42 @@ open class DeckPicker :
|
||||
CollectionPermissionScreenLauncher,
|
||||
ExportDialogsFactoryProvider {
|
||||
// Short animation duration from system
|
||||
private var mShortAnimDuration = 0
|
||||
private var mBackButtonPressedToExit = false
|
||||
private lateinit var mDeckPickerContent: RelativeLayout
|
||||
private var shortAnimDuration = 0
|
||||
private var backButtonPressedToExit = false
|
||||
private lateinit var deckPickerContent: RelativeLayout
|
||||
|
||||
@Suppress("Deprecation") // TODO: Encapsulate ProgressDialog within a class to limit the use of deprecated functionality
|
||||
var mProgressDialog: android.app.ProgressDialog? = null
|
||||
private var mStudyoptionsFrame: View? = null // not lateInit - can be null
|
||||
var progressDialog: android.app.ProgressDialog? = null
|
||||
private var studyoptionsFrame: View? = null // not lateInit - can be null
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
lateinit var recyclerView: RecyclerView
|
||||
private lateinit var mRecyclerViewLayoutManager: LinearLayoutManager
|
||||
private lateinit var mDeckListAdapter: DeckAdapter
|
||||
private val mSnackbarShowHideCallback = Snackbar.Callback()
|
||||
lateinit var mExportingDelegate: ActivityExportingDelegate
|
||||
private lateinit var mNoDecksPlaceholder: LinearLayout
|
||||
lateinit var mPullToSyncWrapper: SwipeRefreshLayout
|
||||
private lateinit var recyclerViewLayoutManager: LinearLayoutManager
|
||||
private lateinit var deckListAdapter: DeckAdapter
|
||||
private val snackbarShowHideCallback = Snackbar.Callback()
|
||||
lateinit var exportingDelegate: ActivityExportingDelegate
|
||||
private lateinit var noDecksPlaceholder: LinearLayout
|
||||
lateinit var pullToSyncWrapper: SwipeRefreshLayout
|
||||
private set
|
||||
|
||||
private lateinit var mReviewSummaryTextView: TextView
|
||||
private lateinit var reviewSummaryTextView: TextView
|
||||
|
||||
@KotlinCleanup("make lateinit, but needs more changes")
|
||||
private var mUnmountReceiver: BroadcastReceiver? = null
|
||||
private lateinit var mFloatingActionMenu: DeckPickerFloatingActionMenu
|
||||
private var unmountReceiver: BroadcastReceiver? = null
|
||||
private lateinit var floatingActionMenu: DeckPickerFloatingActionMenu
|
||||
|
||||
// flag asking user to do a full sync which is used in upgrade path
|
||||
private var mRecommendFullSync = false
|
||||
private var recommendFullSync = false
|
||||
|
||||
override val baseSnackbarBuilder: SnackbarBuilder = {
|
||||
anchorView = findViewById<FloatingActionButton>(R.id.fab_main)
|
||||
}
|
||||
|
||||
// flag keeping track of when the app has been paused
|
||||
var mActivityPaused = false
|
||||
var activityPaused = false
|
||||
private set
|
||||
|
||||
// Flag to keep track of startup error
|
||||
private var mStartupError = false
|
||||
private var startupError = false
|
||||
|
||||
/** See [OptionsMenuState]. */
|
||||
@VisibleForTesting
|
||||
@ -239,7 +239,7 @@ open class DeckPicker :
|
||||
* Since syncing closes the database, this flag allows us to avoid doing any
|
||||
* work in onResume that might use the database and go straight to syncing.
|
||||
*/
|
||||
private var mSyncOnResume = false
|
||||
private var syncOnResume = false
|
||||
|
||||
/**
|
||||
* Keep track of which deck was last given focus in the deck list. If we find that this value
|
||||
@ -247,12 +247,12 @@ open class DeckPicker :
|
||||
* deck.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
internal var mFocusedDeck: DeckId = 0
|
||||
internal var focusedDeck: DeckId = 0
|
||||
|
||||
var importColpkgListener: ImportColpkgListener? = null
|
||||
|
||||
private var mToolbarSearchView: SearchView? = null
|
||||
private lateinit var mCustomStudyDialogFactory: CustomStudyDialogFactory
|
||||
private var toolbarSearchView: SearchView? = null
|
||||
private lateinit var customStudyDialogFactory: CustomStudyDialogFactory
|
||||
|
||||
override val permissionScreenLauncher = recreateActivityResultLauncher()
|
||||
|
||||
@ -274,7 +274,7 @@ open class DeckPicker :
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
DeckPickerActivityResultCallback {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
mSyncOnResume = true
|
||||
syncOnResume = true
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -332,11 +332,11 @@ open class DeckPicker :
|
||||
// ----------------------------------------------------------------------------
|
||||
// LISTENERS
|
||||
// ----------------------------------------------------------------------------
|
||||
private val mDeckExpanderClickListener = View.OnClickListener { view: View ->
|
||||
private val deckExpanderClickListener = View.OnClickListener { view: View ->
|
||||
launchCatchingTask { toggleDeckExpand(view.tag as Long) }
|
||||
}
|
||||
private val mDeckClickListener = View.OnClickListener { v: View -> onDeckClick(v, DeckSelectionType.DEFAULT) }
|
||||
private val mCountsClickListener = View.OnClickListener { v: View -> onDeckClick(v, DeckSelectionType.SHOW_STUDY_OPTIONS) }
|
||||
private val deckClickListener = View.OnClickListener { v: View -> onDeckClick(v, DeckSelectionType.DEFAULT) }
|
||||
private val countsClickListener = View.OnClickListener { v: View -> onDeckClick(v, DeckSelectionType.SHOW_STUDY_OPTIONS) }
|
||||
private fun onDeckClick(v: View, selectionType: DeckSelectionType) {
|
||||
val deckId = v.tag as Long
|
||||
Timber.i("DeckPicker:: Selected deck with id %d", deckId)
|
||||
@ -345,13 +345,13 @@ open class DeckPicker :
|
||||
if (fragmented) {
|
||||
// Calling notifyDataSetChanged() will update the color of the selected deck.
|
||||
// This interferes with the ripple effect, so we don't do it if lollipop and not tablet view
|
||||
mDeckListAdapter.notifyDataSetChanged()
|
||||
deckListAdapter.notifyDataSetChanged()
|
||||
updateDeckList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val mDeckLongClickListener = OnLongClickListener { v ->
|
||||
private val deckLongClickListener = OnLongClickListener { v ->
|
||||
val deckId = v.tag as DeckId
|
||||
Timber.i("DeckPicker:: Long tapped on deck with id %d", deckId)
|
||||
launchCatchingTask {
|
||||
@ -385,8 +385,8 @@ open class DeckPicker :
|
||||
if (showedActivityFailedScreen(savedInstanceState)) {
|
||||
return
|
||||
}
|
||||
mExportingDelegate = ActivityExportingDelegate(this) { getColUnsafe }
|
||||
mCustomStudyDialogFactory = CustomStudyDialogFactory({ getColUnsafe }, this).attachToActivity(this)
|
||||
exportingDelegate = ActivityExportingDelegate(this) { getColUnsafe }
|
||||
customStudyDialogFactory = CustomStudyDialogFactory({ getColUnsafe }, this).attachToActivity(this)
|
||||
|
||||
// Then set theme and content view
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -404,7 +404,7 @@ open class DeckPicker :
|
||||
}
|
||||
if (intent.hasExtra(INTENT_SYNC_FROM_LOGIN)) {
|
||||
Timber.d("launched from introduction activity login: syncing")
|
||||
mSyncOnResume = true
|
||||
syncOnResume = true
|
||||
}
|
||||
|
||||
setContentView(R.layout.homescreen)
|
||||
@ -412,12 +412,12 @@ open class DeckPicker :
|
||||
val mainView = findViewById<View>(android.R.id.content)
|
||||
|
||||
// check, if tablet layout
|
||||
mStudyoptionsFrame = findViewById(R.id.studyoptions_fragment)
|
||||
studyoptionsFrame = findViewById(R.id.studyoptions_fragment)
|
||||
// set protected variable from NavigationDrawerActivity
|
||||
fragmented = mStudyoptionsFrame != null && mStudyoptionsFrame!!.visibility == View.VISIBLE
|
||||
fragmented = studyoptionsFrame != null && studyoptionsFrame!!.visibility == View.VISIBLE
|
||||
|
||||
// Open StudyOptionsFragment if in fragmented mode
|
||||
if (fragmented && !mStartupError) {
|
||||
if (fragmented && !startupError) {
|
||||
loadStudyOptionsFragment(false)
|
||||
}
|
||||
registerExternalStorageListener()
|
||||
@ -426,20 +426,20 @@ open class DeckPicker :
|
||||
initNavigationDrawer(mainView)
|
||||
title = resources.getString(R.string.app_name)
|
||||
|
||||
mDeckPickerContent = findViewById(R.id.deck_picker_content)
|
||||
deckPickerContent = findViewById(R.id.deck_picker_content)
|
||||
recyclerView = findViewById(R.id.files)
|
||||
mNoDecksPlaceholder = findViewById(R.id.no_decks_placeholder)
|
||||
noDecksPlaceholder = findViewById(R.id.no_decks_placeholder)
|
||||
|
||||
mDeckPickerContent.visibility = View.GONE
|
||||
mNoDecksPlaceholder.visibility = View.GONE
|
||||
deckPickerContent.visibility = View.GONE
|
||||
noDecksPlaceholder.visibility = View.GONE
|
||||
|
||||
// specify a LinearLayoutManager and set up item dividers for the RecyclerView
|
||||
mRecyclerViewLayoutManager = LinearLayoutManager(this)
|
||||
recyclerView.layoutManager = mRecyclerViewLayoutManager
|
||||
recyclerViewLayoutManager = LinearLayoutManager(this)
|
||||
recyclerView.layoutManager = recyclerViewLayoutManager
|
||||
val ta = this.obtainStyledAttributes(intArrayOf(R.attr.deckDivider))
|
||||
val divider = ta.getDrawable(0)
|
||||
ta.recycle()
|
||||
val dividerDecorator = DividerItemDecoration(this, mRecyclerViewLayoutManager.orientation)
|
||||
val dividerDecorator = DividerItemDecoration(this, recyclerViewLayoutManager.orientation)
|
||||
dividerDecorator.setDrawable(divider!!)
|
||||
recyclerView.addItemDecoration(dividerDecorator)
|
||||
|
||||
@ -456,37 +456,37 @@ open class DeckPicker :
|
||||
Timber.w(e, "Failed to apply background")
|
||||
showThemedToast(this, getString(R.string.failed_to_apply_background_image, e.localizedMessage), false)
|
||||
}
|
||||
mExportingDelegate.onRestoreInstanceState(savedInstanceState)
|
||||
exportingDelegate.onRestoreInstanceState(savedInstanceState)
|
||||
|
||||
// create and set an adapter for the RecyclerView
|
||||
mDeckListAdapter = DeckAdapter(layoutInflater, this).apply {
|
||||
setDeckClickListener(mDeckClickListener)
|
||||
setCountsClickListener(mCountsClickListener)
|
||||
setDeckExpanderClickListener(mDeckExpanderClickListener)
|
||||
setDeckLongClickListener(mDeckLongClickListener)
|
||||
deckListAdapter = DeckAdapter(layoutInflater, this).apply {
|
||||
setDeckClickListener(deckClickListener)
|
||||
setCountsClickListener(countsClickListener)
|
||||
setDeckExpanderClickListener(deckExpanderClickListener)
|
||||
setDeckLongClickListener(deckLongClickListener)
|
||||
enablePartialTransparencyForBackground(hasDeckPickerBackground)
|
||||
}
|
||||
recyclerView.adapter = mDeckListAdapter
|
||||
recyclerView.adapter = deckListAdapter
|
||||
|
||||
mPullToSyncWrapper = findViewById<SwipeRefreshLayout?>(R.id.pull_to_sync_wrapper).apply {
|
||||
pullToSyncWrapper = findViewById<SwipeRefreshLayout?>(R.id.pull_to_sync_wrapper).apply {
|
||||
setDistanceToTriggerSync(SWIPE_TO_SYNC_TRIGGER_DISTANCE)
|
||||
setOnRefreshListener {
|
||||
Timber.i("Pull to Sync: Syncing")
|
||||
mPullToSyncWrapper.isRefreshing = false
|
||||
pullToSyncWrapper.isRefreshing = false
|
||||
sync()
|
||||
}
|
||||
viewTreeObserver.addOnScrollChangedListener {
|
||||
mPullToSyncWrapper.isEnabled = mRecyclerViewLayoutManager.findFirstCompletelyVisibleItemPosition() == 0
|
||||
pullToSyncWrapper.isEnabled = recyclerViewLayoutManager.findFirstCompletelyVisibleItemPosition() == 0
|
||||
}
|
||||
}
|
||||
// Setup the FloatingActionButtons, should work everywhere with min API >= 15
|
||||
mFloatingActionMenu = DeckPickerFloatingActionMenu(this, view, this)
|
||||
floatingActionMenu = DeckPickerFloatingActionMenu(this, view, this)
|
||||
|
||||
mReviewSummaryTextView = findViewById(R.id.today_stats_text_view)
|
||||
reviewSummaryTextView = findViewById(R.id.today_stats_text_view)
|
||||
|
||||
mShortAnimDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||
shortAnimDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||
|
||||
Onboarding.DeckPicker(this, mRecyclerViewLayoutManager).onCreate()
|
||||
Onboarding.DeckPicker(this, recyclerViewLayoutManager).onCreate()
|
||||
|
||||
launchShowingHidingEssentialFileMigrationProgressDialog()
|
||||
if (BuildConfig.DEBUG) {
|
||||
@ -668,7 +668,7 @@ open class DeckPicker :
|
||||
|
||||
Timber.d("handleStartup: Continuing. unaffected by storage migration")
|
||||
val failure = InitialActivity.getStartupFailureType(this)
|
||||
mStartupError = if (failure == null) {
|
||||
startupError = if (failure == null) {
|
||||
// Show any necessary dialogs (e.g. changelog, special messages, etc)
|
||||
val sharedPrefs = this.sharedPrefs()
|
||||
showStartupScreensAndDialogs(sharedPrefs, 0)
|
||||
@ -759,11 +759,11 @@ open class DeckPicker :
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
Timber.d("onCreateOptionsMenu()")
|
||||
mFloatingActionMenu.closeFloatingActionMenu(applyRiseAndShrinkAnimation = false)
|
||||
floatingActionMenu.closeFloatingActionMenu(applyRiseAndShrinkAnimation = false)
|
||||
menuInflater.inflate(R.menu.deck_picker, menu)
|
||||
menu.findItem(R.id.action_export)?.title = TR.exportingExport()
|
||||
setupSearchIcon(menu.findItem(R.id.deck_picker_action_filter))
|
||||
mToolbarSearchView = menu.findItem(R.id.deck_picker_action_filter).actionView as SearchView
|
||||
toolbarSearchView = menu.findItem(R.id.deck_picker_action_filter).actionView as SearchView
|
||||
// redraw menu synchronously to avoid flicker
|
||||
updateMenuFromState(menu)
|
||||
// ...then launch a task to possibly update the visible icons.
|
||||
@ -870,7 +870,7 @@ open class DeckPicker :
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
Timber.i("DeckPicker:: SearchItem opened")
|
||||
// Hide the floating action button if it is visible
|
||||
mFloatingActionMenu.hideFloatingActionButton()
|
||||
floatingActionMenu.hideFloatingActionButton()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -878,7 +878,7 @@ open class DeckPicker :
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
Timber.i("DeckPicker:: SearchItem closed")
|
||||
// Show the floating action button if it is hidden
|
||||
mFloatingActionMenu.showFloatingActionButton()
|
||||
floatingActionMenu.showFloatingActionButton()
|
||||
return true
|
||||
}
|
||||
})
|
||||
@ -1082,7 +1082,7 @@ open class DeckPicker :
|
||||
}
|
||||
}
|
||||
|
||||
override fun exportDialogsFactory(): ExportDialogsFactory = mExportingDelegate.mDialogsFactory
|
||||
override fun exportDialogsFactory(): ExportDialogsFactory = exportingDelegate.dialogsFactory
|
||||
|
||||
fun exportCollection() {
|
||||
if (mediaMigrationIsInProgress(this)) {
|
||||
@ -1102,7 +1102,7 @@ open class DeckPicker :
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
mActivityPaused = false
|
||||
activityPaused = false
|
||||
// stop onResume() processing the message.
|
||||
// we need to process the message after `loadDeckCounts` is added in refreshState
|
||||
// As `loadDeckCounts` is cancelled in `migrate()`
|
||||
@ -1116,10 +1116,10 @@ open class DeckPicker :
|
||||
|
||||
fun refreshState() {
|
||||
// Due to the App Introduction, this may be called before permission has been granted.
|
||||
if (mSyncOnResume && hasCollectionStoragePermissions()) {
|
||||
if (syncOnResume && hasCollectionStoragePermissions()) {
|
||||
Timber.i("Performing Sync on Resume")
|
||||
sync()
|
||||
mSyncOnResume = false
|
||||
syncOnResume = false
|
||||
} else {
|
||||
selectNavigationItem(R.id.nav_decks)
|
||||
updateDeckList()
|
||||
@ -1131,20 +1131,20 @@ open class DeckPicker :
|
||||
|
||||
public override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
||||
super.onSaveInstanceState(savedInstanceState)
|
||||
savedInstanceState.putBoolean("mIsFABOpen", mFloatingActionMenu.isFABOpen)
|
||||
savedInstanceState.putBoolean("mIsFABOpen", floatingActionMenu.isFABOpen)
|
||||
savedInstanceState.putBoolean("migrateStorageAfterMediaSyncCompleted", migrateStorageAfterMediaSyncCompleted)
|
||||
importColpkgListener?.let {
|
||||
if (it is DatabaseRestorationListener) {
|
||||
savedInstanceState.getString("dbRestorationPath", it.newAnkiDroidDirectory)
|
||||
}
|
||||
}
|
||||
mExportingDelegate.onSaveInstanceState(savedInstanceState)
|
||||
exportingDelegate.onSaveInstanceState(savedInstanceState)
|
||||
savedInstanceState.putSerializable("mediaUsnOnConflict", mediaUsnOnConflict)
|
||||
}
|
||||
|
||||
public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
mFloatingActionMenu.isFABOpen = savedInstanceState.getBoolean("mIsFABOpen")
|
||||
floatingActionMenu.isFABOpen = savedInstanceState.getBoolean("mIsFABOpen")
|
||||
migrateStorageAfterMediaSyncCompleted = savedInstanceState.getBoolean("migrateStorageAfterMediaSyncCompleted")
|
||||
savedInstanceState.getString("dbRestorationPath")?.let { path ->
|
||||
CollectionHelper.ankiDroidDirectoryOverride = path
|
||||
@ -1154,7 +1154,7 @@ open class DeckPicker :
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
mActivityPaused = true
|
||||
activityPaused = true
|
||||
// The deck count will be computed on resume. No need to compute it now
|
||||
loadDeckCounts?.cancel()
|
||||
super.onPause()
|
||||
@ -1167,11 +1167,11 @@ open class DeckPicker :
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (mUnmountReceiver != null) {
|
||||
unregisterReceiver(mUnmountReceiver)
|
||||
if (unmountReceiver != null) {
|
||||
unregisterReceiver(unmountReceiver)
|
||||
}
|
||||
if (mProgressDialog != null && mProgressDialog!!.isShowing) {
|
||||
mProgressDialog!!.dismiss()
|
||||
if (progressDialog != null && progressDialog!!.isShowing) {
|
||||
progressDialog!!.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1204,29 +1204,29 @@ open class DeckPicker :
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
Timber.i("Back key pressed")
|
||||
if (mFloatingActionMenu.isFABOpen) {
|
||||
mFloatingActionMenu.closeFloatingActionMenu(applyRiseAndShrinkAnimation = true)
|
||||
if (floatingActionMenu.isFABOpen) {
|
||||
floatingActionMenu.closeFloatingActionMenu(applyRiseAndShrinkAnimation = true)
|
||||
} else {
|
||||
if (!preferences.getBoolean(
|
||||
"exitViaDoubleTapBack",
|
||||
false
|
||||
) || mBackButtonPressedToExit
|
||||
) || backButtonPressedToExit
|
||||
) {
|
||||
automaticSync()
|
||||
finish()
|
||||
} else {
|
||||
showSnackbar(R.string.back_pressed_once, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
mBackButtonPressedToExit = true
|
||||
backButtonPressedToExit = true
|
||||
HandlerUtils.executeFunctionWithDelay(Consts.SHORT_TOAST_DURATION) {
|
||||
mBackButtonPressedToExit = false
|
||||
backButtonPressedToExit = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (mToolbarSearchView != null && mToolbarSearchView!!.hasFocus()) {
|
||||
if (toolbarSearchView != null && toolbarSearchView!!.hasFocus()) {
|
||||
Timber.d("Skipping keypress: search action bar is focused")
|
||||
return true
|
||||
}
|
||||
@ -1274,8 +1274,8 @@ open class DeckPicker :
|
||||
}
|
||||
|
||||
// Force a full sync if flag was set in upgrade path, asking the user to confirm if necessary
|
||||
if (mRecommendFullSync) {
|
||||
mRecommendFullSync = false
|
||||
if (recommendFullSync) {
|
||||
recommendFullSync = false
|
||||
try {
|
||||
getColUnsafe.modSchema()
|
||||
} catch (e: ConfirmModSchemaException) {
|
||||
@ -1372,7 +1372,7 @@ open class DeckPicker :
|
||||
// Recommend the user to do a full-sync if they're upgrading from before 2.3.1beta8
|
||||
if (previous < 20301208) {
|
||||
Timber.i("Recommend the user to do a full-sync")
|
||||
mRecommendFullSync = true
|
||||
recommendFullSync = true
|
||||
}
|
||||
|
||||
// Fix "font-family" definition in templates created by AnkiDroid before 2.6alpha23
|
||||
@ -1676,7 +1676,7 @@ open class DeckPicker :
|
||||
val hkey = preferences.getString("hkey", "")
|
||||
if (hkey!!.isEmpty()) {
|
||||
Timber.w("User not logged in")
|
||||
mPullToSyncWrapper.isRefreshing = false
|
||||
pullToSyncWrapper.isRefreshing = false
|
||||
showSyncErrorDialog(SyncErrorDialog.DIALOG_USER_NOT_LOGGED_IN_SYNC)
|
||||
return
|
||||
}
|
||||
@ -1753,8 +1753,8 @@ open class DeckPicker :
|
||||
* Show a message when the SD card is ejected
|
||||
*/
|
||||
private fun registerExternalStorageListener() {
|
||||
if (mUnmountReceiver == null) {
|
||||
mUnmountReceiver = object : BroadcastReceiver() {
|
||||
if (unmountReceiver == null) {
|
||||
unmountReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (intent.action == SdCardReceiver.MEDIA_EJECT) {
|
||||
onSdCardNotMounted()
|
||||
@ -1766,7 +1766,7 @@ open class DeckPicker :
|
||||
val iFilter = IntentFilter()
|
||||
iFilter.addAction(SdCardReceiver.MEDIA_EJECT)
|
||||
iFilter.addAction(SdCardReceiver.MEDIA_MOUNT)
|
||||
registerReceiver(mUnmountReceiver, iFilter)
|
||||
registerReceiver(unmountReceiver, iFilter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1840,7 +1840,7 @@ open class DeckPicker :
|
||||
@NeedsTest("14608: Ensure that the deck options refer to the selected deck")
|
||||
private suspend fun handleDeckSelection(did: DeckId, selectionType: DeckSelectionType) {
|
||||
fun showEmptyDeckSnackbar() = showSnackbar(R.string.empty_deck) {
|
||||
addCallback(mSnackbarShowHideCallback)
|
||||
addCallback(snackbarShowHideCallback)
|
||||
setAction(R.string.menu_add) { addNote() }
|
||||
}
|
||||
|
||||
@ -1865,8 +1865,8 @@ open class DeckPicker :
|
||||
withCol { decks.select(did) }
|
||||
// Also forget the last deck used by the Browser
|
||||
CardBrowser.clearLastDeckId()
|
||||
mFocusedDeck = did
|
||||
val deck = mDeckListAdapter.getNodeByDid(did)
|
||||
focusedDeck = did
|
||||
val deck = deckListAdapter.getNodeByDid(did)
|
||||
if (deck.hasCardsReadyToStudy()) {
|
||||
openReviewerOrStudyOptions(selectionType)
|
||||
return
|
||||
@ -1893,8 +1893,8 @@ open class DeckPicker :
|
||||
* @param did The deck ID of the deck to select.
|
||||
*/
|
||||
private fun scrollDecklistToDeck(did: DeckId) {
|
||||
val position = mDeckListAdapter.findDeckPosition(did)
|
||||
mRecyclerViewLayoutManager.scrollToPositionWithOffset(position, recyclerView.height / 2)
|
||||
val position = deckListAdapter.findDeckPosition(did)
|
||||
recyclerViewLayoutManager.scrollToPositionWithOffset(position, recyclerView.height / 2)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1933,14 +1933,14 @@ open class DeckPicker :
|
||||
hideProgressBar()
|
||||
// Make sure the fragment is visible
|
||||
if (fragmented) {
|
||||
mStudyoptionsFrame!!.visibility = View.VISIBLE
|
||||
studyoptionsFrame!!.visibility = View.VISIBLE
|
||||
}
|
||||
dueTree = result
|
||||
launchCatchingTask { renderPage(collectionIsEmpty) }
|
||||
// Update the mini statistics bar as well
|
||||
mReviewSummaryTextView.setSingleLine()
|
||||
reviewSummaryTextView.setSingleLine()
|
||||
launchCatchingTask {
|
||||
mReviewSummaryTextView.text = withCol {
|
||||
reviewSummaryTextView.text = withCol {
|
||||
sched.studiedToday()
|
||||
}
|
||||
}
|
||||
@ -1960,50 +1960,50 @@ open class DeckPicker :
|
||||
// Check if default deck is the only available and there are no cards
|
||||
val isEmpty = tree.children.size == 1 && tree.children[0].did == 1L && collectionIsEmpty
|
||||
if (animationDisabled()) {
|
||||
mDeckPickerContent.visibility = if (isEmpty) View.GONE else View.VISIBLE
|
||||
mNoDecksPlaceholder.visibility = if (isEmpty) View.VISIBLE else View.GONE
|
||||
deckPickerContent.visibility = if (isEmpty) View.GONE else View.VISIBLE
|
||||
noDecksPlaceholder.visibility = if (isEmpty) View.VISIBLE else View.GONE
|
||||
} else {
|
||||
val translation = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
8f,
|
||||
resources.displayMetrics
|
||||
)
|
||||
val decksListShown = mDeckPickerContent.visibility == View.VISIBLE
|
||||
val placeholderShown = mNoDecksPlaceholder.visibility == View.VISIBLE
|
||||
val decksListShown = deckPickerContent.visibility == View.VISIBLE
|
||||
val placeholderShown = noDecksPlaceholder.visibility == View.VISIBLE
|
||||
if (isEmpty) {
|
||||
if (decksListShown) {
|
||||
fadeOut(mDeckPickerContent, mShortAnimDuration, translation)
|
||||
fadeOut(deckPickerContent, shortAnimDuration, translation)
|
||||
}
|
||||
if (!placeholderShown) {
|
||||
fadeIn(mNoDecksPlaceholder, mShortAnimDuration, translation).startDelay = if (decksListShown) mShortAnimDuration * 2.toLong() else 0.toLong()
|
||||
fadeIn(noDecksPlaceholder, shortAnimDuration, translation).startDelay = if (decksListShown) shortAnimDuration * 2.toLong() else 0.toLong()
|
||||
}
|
||||
} else {
|
||||
if (!decksListShown) {
|
||||
fadeIn(mDeckPickerContent, mShortAnimDuration, translation).startDelay = if (placeholderShown) mShortAnimDuration * 2.toLong() else 0.toLong()
|
||||
fadeIn(deckPickerContent, shortAnimDuration, translation).startDelay = if (placeholderShown) shortAnimDuration * 2.toLong() else 0.toLong()
|
||||
}
|
||||
if (placeholderShown) {
|
||||
fadeOut(mNoDecksPlaceholder, mShortAnimDuration, translation)
|
||||
fadeOut(noDecksPlaceholder, shortAnimDuration, translation)
|
||||
}
|
||||
}
|
||||
}
|
||||
val currentFilter = if (mToolbarSearchView != null) mToolbarSearchView!!.query else null
|
||||
val currentFilter = if (toolbarSearchView != null) toolbarSearchView!!.query else null
|
||||
|
||||
if (isEmpty) {
|
||||
if (supportActionBar != null) {
|
||||
supportActionBar!!.subtitle = null
|
||||
}
|
||||
if (mToolbarSearchView != null) {
|
||||
mDeckListAdapter.filter?.filter(currentFilter)
|
||||
if (toolbarSearchView != null) {
|
||||
deckListAdapter.filter?.filter(currentFilter)
|
||||
}
|
||||
Timber.d("Not rendering deck list as there are no cards")
|
||||
// We're done here
|
||||
return
|
||||
}
|
||||
mDeckListAdapter.buildDeckList(tree, currentFilter)
|
||||
deckListAdapter.buildDeckList(tree, currentFilter)
|
||||
|
||||
// Set the "x due" subtitle
|
||||
try {
|
||||
val due = mDeckListAdapter.due
|
||||
val due = deckListAdapter.due
|
||||
val res = resources
|
||||
|
||||
if (due != null && supportActionBar != null) {
|
||||
@ -2019,9 +2019,9 @@ open class DeckPicker :
|
||||
Timber.e(e, "RuntimeException setting time remaining")
|
||||
}
|
||||
val current = withCol { decks.current().optLong("id") }
|
||||
if (mFocusedDeck != current) {
|
||||
if (focusedDeck != current) {
|
||||
scrollDecklistToDeck(current)
|
||||
mFocusedDeck = current
|
||||
focusedDeck = current
|
||||
}
|
||||
}
|
||||
|
||||
@ -2086,7 +2086,7 @@ open class DeckPicker :
|
||||
createDeckDialog.deckName = currentName
|
||||
createDeckDialog.setOnNewDeckCreated {
|
||||
dismissAllDialogFragments()
|
||||
mDeckListAdapter.notifyDataSetChanged()
|
||||
deckListAdapter.notifyDataSetChanged()
|
||||
updateDeckList()
|
||||
if (fragmented) {
|
||||
loadStudyOptionsFragment(false)
|
||||
@ -2209,7 +2209,7 @@ open class DeckPicker :
|
||||
createDeckDialog.setOnNewDeckCreated {
|
||||
// a deck was created
|
||||
dismissAllDialogFragments()
|
||||
mDeckListAdapter.notifyDataSetChanged()
|
||||
deckListAdapter.notifyDataSetChanged()
|
||||
updateDeckList()
|
||||
if (fragmented) {
|
||||
loadStudyOptionsFragment(false)
|
||||
@ -2224,13 +2224,13 @@ open class DeckPicker :
|
||||
*/
|
||||
@get:VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
val visibleDeckCount: Int
|
||||
get() = mDeckListAdapter.itemCount
|
||||
get() = deckListAdapter.itemCount
|
||||
|
||||
/**
|
||||
* Check if at least one deck is being displayed.
|
||||
*/
|
||||
fun hasAtLeastOneDeckBeingDisplayed(): Boolean {
|
||||
return mDeckListAdapter.itemCount > 0 && mRecyclerViewLayoutManager.getChildAt(0) != null
|
||||
return deckListAdapter.itemCount > 0 && recyclerViewLayoutManager.getChildAt(0) != null
|
||||
}
|
||||
|
||||
private enum class DeckSelectionType {
|
||||
@ -2314,7 +2314,7 @@ open class DeckPicker :
|
||||
return
|
||||
}
|
||||
|
||||
if (mActivityPaused) {
|
||||
if (activityPaused) {
|
||||
sendNotificationForAsyncOperation(MigrateStorageOnSyncSuccess(this.resources), Channel.SYNC)
|
||||
return
|
||||
}
|
||||
@ -2527,7 +2527,7 @@ open class DeckPicker :
|
||||
sched.hasCardsTodayAfterStudyAheadLimit() -> CompletedDeckStatus.LEARN_AHEAD_LIMIT_REACHED
|
||||
sched.newDue() || sched.revDue() -> CompletedDeckStatus.LEARN_AHEAD_LIMIT_REACHED
|
||||
decks.isDyn(did) -> CompletedDeckStatus.DYNAMIC_DECK_NO_LIMITS_REACHED
|
||||
mDeckListAdapter.getNodeByDid(did).children.isEmpty() && isEmptyDeck(did) -> CompletedDeckStatus.EMPTY_REGULAR_DECK
|
||||
deckListAdapter.getNodeByDid(did).children.isEmpty() && isEmptyDeck(did) -> CompletedDeckStatus.EMPTY_REGULAR_DECK
|
||||
else -> CompletedDeckStatus.REGULAR_DECK_NO_MORE_CARDS_TODAY
|
||||
}
|
||||
}
|
||||
|
@ -43,15 +43,18 @@ import com.ichi2.ui.AnimationUtil.expandView
|
||||
import com.ichi2.utils.KotlinCleanup
|
||||
import java.util.*
|
||||
|
||||
@KotlinCleanup("replace _name with `field`")
|
||||
@KotlinCleanup("remove setTypeface")
|
||||
@KotlinCleanup("replace setEnableAnimation with var")
|
||||
class FieldEditLine : FrameLayout {
|
||||
val editText: FieldEditText
|
||||
private val mLabel: TextView
|
||||
private val label: TextView
|
||||
val toggleSticky: ImageButton
|
||||
val mediaButton: ImageButton
|
||||
private val mExpandButton: ImageButton
|
||||
private var mName: String? = null
|
||||
private var mExpansionState = ExpansionState.EXPANDED
|
||||
private var mEnableAnimation = true
|
||||
private val expandButton: ImageButton
|
||||
private var _name: String? = null
|
||||
private var expansionState = ExpansionState.EXPANDED
|
||||
private var enableAnimation = true
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
@ -62,41 +65,41 @@ class FieldEditLine : FrameLayout {
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.card_multimedia_editline, this, true)
|
||||
editText = findViewById(R.id.id_note_editText)
|
||||
mLabel = findViewById(R.id.id_label)
|
||||
label = findViewById(R.id.id_label)
|
||||
toggleSticky = findViewById(R.id.id_toggle_sticky_button)
|
||||
mediaButton = findViewById(R.id.id_media_button)
|
||||
val constraintLayout: ConstraintLayout = findViewById(R.id.constraint_layout)
|
||||
mExpandButton = findViewById(R.id.id_expand_button)
|
||||
expandButton = findViewById(R.id.id_expand_button)
|
||||
// 7433 -
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
editText.id = ViewCompat.generateViewId()
|
||||
toggleSticky.id = ViewCompat.generateViewId()
|
||||
mediaButton.id = ViewCompat.generateViewId()
|
||||
mExpandButton.id = ViewCompat.generateViewId()
|
||||
expandButton.id = ViewCompat.generateViewId()
|
||||
editText.nextFocusForwardId = toggleSticky.id
|
||||
toggleSticky.nextFocusForwardId = mediaButton.id
|
||||
mediaButton.nextFocusForwardId = mExpandButton.id
|
||||
mediaButton.nextFocusForwardId = expandButton.id
|
||||
ConstraintSet().apply {
|
||||
clone(constraintLayout)
|
||||
connect(toggleSticky.id, ConstraintSet.END, mediaButton.id, ConstraintSet.START)
|
||||
connect(mediaButton.id, ConstraintSet.END, mExpandButton.id, ConstraintSet.START)
|
||||
connect(mediaButton.id, ConstraintSet.END, expandButton.id, ConstraintSet.START)
|
||||
applyTo(constraintLayout)
|
||||
}
|
||||
}
|
||||
setExpanderBackgroundImage()
|
||||
mExpandButton.setOnClickListener { toggleExpansionState() }
|
||||
expandButton.setOnClickListener { toggleExpansionState() }
|
||||
editText.init()
|
||||
mLabel.setPadding(getDensityAdjustedValue(context, 3.4f).toInt(), 0, 0, 0)
|
||||
label.setPadding(getDensityAdjustedValue(context, 3.4f).toInt(), 0, 0, 0)
|
||||
}
|
||||
|
||||
private fun toggleExpansionState() {
|
||||
mExpansionState = when (mExpansionState) {
|
||||
expansionState = when (expansionState) {
|
||||
ExpansionState.EXPANDED -> {
|
||||
collapseView(editText, mEnableAnimation)
|
||||
collapseView(editText, enableAnimation)
|
||||
ExpansionState.COLLAPSED
|
||||
}
|
||||
ExpansionState.COLLAPSED -> {
|
||||
expandView(editText, mEnableAnimation)
|
||||
expandView(editText, enableAnimation)
|
||||
ExpansionState.EXPANDED
|
||||
}
|
||||
}
|
||||
@ -104,9 +107,9 @@ class FieldEditLine : FrameLayout {
|
||||
}
|
||||
|
||||
private fun setExpanderBackgroundImage() {
|
||||
when (mExpansionState) {
|
||||
ExpansionState.COLLAPSED -> mExpandButton.background = getBackgroundImage(R.drawable.ic_expand_more_black_24dp_xml)
|
||||
ExpansionState.EXPANDED -> mExpandButton.background = getBackgroundImage(R.drawable.ic_expand_less_black_24dp)
|
||||
when (expansionState) {
|
||||
ExpansionState.COLLAPSED -> expandButton.background = getBackgroundImage(R.drawable.ic_expand_more_black_24dp_xml)
|
||||
ExpansionState.EXPANDED -> expandButton.background = getBackgroundImage(R.drawable.ic_expand_less_black_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,19 +143,19 @@ class FieldEditLine : FrameLayout {
|
||||
}
|
||||
|
||||
fun setEnableAnimation(value: Boolean) {
|
||||
mEnableAnimation = value
|
||||
enableAnimation = value
|
||||
}
|
||||
|
||||
var name: String?
|
||||
get() = mName
|
||||
get() = _name
|
||||
set(name) {
|
||||
mName = name
|
||||
_name = name
|
||||
editText.contentDescription = name
|
||||
mLabel.text = name
|
||||
label.text = name
|
||||
}
|
||||
|
||||
val lastViewInTabOrder: View
|
||||
get() = mExpandButton
|
||||
get() = expandButton
|
||||
|
||||
fun loadState(state: AbsSavedState) {
|
||||
onRestoreInstanceState(state)
|
||||
@ -175,11 +178,11 @@ class FieldEditLine : FrameLayout {
|
||||
savedState.editTextId = editText.id
|
||||
savedState.toggleStickyId = toggleSticky.id
|
||||
savedState.mediaButtonId = mediaButton.id
|
||||
savedState.expandButtonId = mExpandButton.id
|
||||
savedState.expandButtonId = expandButton.id
|
||||
for (i in 0 until childCount) {
|
||||
getChildAt(i).saveHierarchyState(savedState.childrenStates)
|
||||
}
|
||||
savedState.expansionState = mExpansionState
|
||||
savedState.expansionState = expansionState
|
||||
return savedState
|
||||
}
|
||||
|
||||
@ -191,11 +194,11 @@ class FieldEditLine : FrameLayout {
|
||||
val editTextId = editText.id
|
||||
val toggleStickyId = toggleSticky.id
|
||||
val mediaButtonId = mediaButton.id
|
||||
val expandButtonId = mExpandButton.id
|
||||
val expandButtonId = expandButton.id
|
||||
editText.id = state.editTextId
|
||||
toggleSticky.id = state.toggleStickyId
|
||||
mediaButton.id = state.mediaButtonId
|
||||
mExpandButton.id = state.expandButtonId
|
||||
expandButton.id = state.expandButtonId
|
||||
super.onRestoreInstanceState(state.superState)
|
||||
for (i in 0 until childCount) {
|
||||
getChildAt(i).restoreHierarchyState(state.childrenStates)
|
||||
@ -203,11 +206,11 @@ class FieldEditLine : FrameLayout {
|
||||
editText.id = editTextId
|
||||
toggleSticky.id = toggleStickyId
|
||||
mediaButton.id = mediaButtonId
|
||||
mExpandButton.id = expandButtonId
|
||||
if (mExpansionState != state.expansionState) {
|
||||
expandButton.id = expandButtonId
|
||||
if (expansionState != state.expansionState) {
|
||||
toggleExpansionState()
|
||||
}
|
||||
mExpansionState = state.expansionState ?: ExpansionState.EXPANDED
|
||||
expansionState = state.expansionState ?: ExpansionState.EXPANDED
|
||||
}
|
||||
|
||||
@KotlinCleanup("convert to parcelable")
|
||||
|
@ -42,30 +42,30 @@ import timber.log.Timber
|
||||
*/
|
||||
|
||||
class Previewer : AbstractFlashcardViewer() {
|
||||
private lateinit var mCardList: LongArray
|
||||
private var mIndex = 0
|
||||
private var mShowingAnswer = false
|
||||
private lateinit var mProgressSeekBar: SeekBar
|
||||
private lateinit var mProgressText: TextView
|
||||
private lateinit var cardList: LongArray
|
||||
private var index = 0
|
||||
private var showingAnswer = false
|
||||
private lateinit var progressSeekBar: SeekBar
|
||||
private lateinit var progressText: TextView
|
||||
|
||||
/** Communication with Browser */
|
||||
private var mReloadRequired = false
|
||||
private var mNoteChanged = false
|
||||
private var reloadRequired = false
|
||||
private var noteChanged = false
|
||||
private var previewLayout: PreviewLayout? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (showedActivityFailedScreen(savedInstanceState)) {
|
||||
return
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
mCardList = requireNotNull(intent.getLongArrayExtra("cardList")) { "'cardList' required" }
|
||||
mIndex = intent.getIntExtra("index", -1)
|
||||
cardList = requireNotNull(intent.getLongArrayExtra("cardList")) { "'cardList' required" }
|
||||
index = intent.getIntExtra("index", -1)
|
||||
if (savedInstanceState != null) {
|
||||
mIndex = savedInstanceState.getInt("index", mIndex)
|
||||
mShowingAnswer = savedInstanceState.getBoolean("showingAnswer", mShowingAnswer)
|
||||
mReloadRequired = savedInstanceState.getBoolean("reloadRequired")
|
||||
mNoteChanged = savedInstanceState.getBoolean("noteChanged")
|
||||
index = savedInstanceState.getInt("index", index)
|
||||
showingAnswer = savedInstanceState.getBoolean("showingAnswer", showingAnswer)
|
||||
reloadRequired = savedInstanceState.getBoolean("reloadRequired")
|
||||
noteChanged = savedInstanceState.getBoolean("noteChanged")
|
||||
}
|
||||
if (mCardList.isEmpty() || mIndex < 0 || mIndex > mCardList.size - 1) {
|
||||
if (cardList.isEmpty() || index < 0 || index > cardList.size - 1) {
|
||||
Timber.e("Previewer started with empty card list or invalid index")
|
||||
finish()
|
||||
return
|
||||
@ -79,26 +79,26 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
private fun initPreviewProgress() {
|
||||
mProgressSeekBar = findViewById(R.id.preview_progress_seek_bar)
|
||||
mProgressText = findViewById(R.id.preview_progress_text)
|
||||
progressSeekBar = findViewById(R.id.preview_progress_seek_bar)
|
||||
progressText = findViewById(R.id.preview_progress_text)
|
||||
val progressLayout = findViewById<LinearLayout>(R.id.preview_progress_layout)
|
||||
|
||||
// Show layout only when the cardList is bigger than 1
|
||||
if (mCardList.size > 1) {
|
||||
if (cardList.size > 1) {
|
||||
progressLayout.visibility = View.VISIBLE
|
||||
mProgressSeekBar.max = mCardList.size - 1
|
||||
progressSeekBar.max = cardList.size - 1
|
||||
setSeekBarListener()
|
||||
updateProgress()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSeekBarListener() {
|
||||
mProgressSeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
progressSeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
mIndex = progress
|
||||
index = progress
|
||||
updateProgress()
|
||||
currentCard = getColUnsafe.getCard(mCardList[mIndex])
|
||||
currentCard = getColUnsafe.getCard(cardList[index])
|
||||
displayCardQuestion()
|
||||
}
|
||||
}
|
||||
@ -108,8 +108,8 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
if (mIndex >= 0 && mIndex < mCardList.size) {
|
||||
currentCard = getColUnsafe.getCard(mCardList[mIndex])
|
||||
if (index >= 0 && index < cardList.size) {
|
||||
currentCard = getColUnsafe.getCard(cardList[index])
|
||||
displayCardQuestion()
|
||||
}
|
||||
}
|
||||
@ -118,9 +118,9 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
|
||||
override fun onCollectionLoaded(col: Collection) {
|
||||
super.onCollectionLoaded(col)
|
||||
currentCard = col.getCard(mCardList[mIndex])
|
||||
currentCard = col.getCard(cardList[index])
|
||||
displayCardQuestion()
|
||||
if (mShowingAnswer) {
|
||||
if (showingAnswer) {
|
||||
displayCardAnswer()
|
||||
}
|
||||
showBackIcon()
|
||||
@ -131,14 +131,14 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
* This occurs as many cards can be deleted when editing a note (from the Card Template Editor) */
|
||||
private fun getNextIndex(newCardList: List<Long>): Int {
|
||||
val validIndices = HashSet(newCardList)
|
||||
for (i in mIndex downTo 0) {
|
||||
if (validIndices.contains(mCardList[i])) {
|
||||
return newCardList.indexOf(mCardList[i])
|
||||
for (i in index downTo 0) {
|
||||
if (validIndices.contains(cardList[i])) {
|
||||
return newCardList.indexOf(cardList[i])
|
||||
}
|
||||
}
|
||||
for (i in mIndex + 1 until validIndices.size) {
|
||||
if (validIndices.contains(mCardList[i])) {
|
||||
return newCardList.indexOf(mCardList[i])
|
||||
for (i in index + 1 until validIndices.size) {
|
||||
if (validIndices.contains(cardList[i])) {
|
||||
return newCardList.indexOf(cardList[i])
|
||||
}
|
||||
}
|
||||
throw IllegalStateException("newCardList was empty")
|
||||
@ -149,7 +149,7 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
topBarLayout!!.visibility = View.GONE
|
||||
findViewById<View>(R.id.answer_options_layout).visibility = View.GONE
|
||||
findViewById<View>(R.id.bottom_area_layout).visibility = View.VISIBLE
|
||||
previewLayout = PreviewLayout.createAndDisplay(this, mToggleAnswerHandler)
|
||||
previewLayout = PreviewLayout.createAndDisplay(this, toggleAnswerHandler)
|
||||
previewLayout!!.setOnNextCard { changePreviewedCard(true) }
|
||||
previewLayout!!.setOnPreviousCard { changePreviewedCard(false) }
|
||||
}
|
||||
@ -173,17 +173,17 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putLongArray("cardList", mCardList)
|
||||
outState.putInt("index", mIndex)
|
||||
outState.putBoolean("showingAnswer", mShowingAnswer)
|
||||
outState.putBoolean("reloadRequired", mReloadRequired)
|
||||
outState.putBoolean("noteChanged", mNoteChanged)
|
||||
outState.putLongArray("cardList", cardList)
|
||||
outState.putInt("index", index)
|
||||
outState.putBoolean("showingAnswer", showingAnswer)
|
||||
outState.putBoolean("reloadRequired", reloadRequired)
|
||||
outState.putBoolean("noteChanged", noteChanged)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun displayCardQuestion() {
|
||||
super.displayCardQuestion()
|
||||
mShowingAnswer = false
|
||||
showingAnswer = false
|
||||
updateButtonsState()
|
||||
}
|
||||
|
||||
@ -191,7 +191,7 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
|
||||
override fun displayCardAnswer() {
|
||||
super.displayCardAnswer()
|
||||
mShowingAnswer = true
|
||||
showingAnswer = true
|
||||
updateButtonsState()
|
||||
}
|
||||
|
||||
@ -209,35 +209,35 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
override fun performReload() {
|
||||
mReloadRequired = true
|
||||
reloadRequired = true
|
||||
setOkResult()
|
||||
val newCardList = getColUnsafe.filterToValidCards(mCardList)
|
||||
val newCardList = getColUnsafe.filterToValidCards(cardList)
|
||||
if (newCardList.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
mIndex = getNextIndex(newCardList)
|
||||
mCardList = newCardList.toLongArray()
|
||||
currentCard = getColUnsafe.getCard(mCardList[mIndex])
|
||||
index = getNextIndex(newCardList)
|
||||
cardList = newCardList.toLongArray()
|
||||
currentCard = getColUnsafe.getCard(cardList[index])
|
||||
displayCardQuestion()
|
||||
}
|
||||
|
||||
override fun onEditedNoteChanged() {
|
||||
super.onEditedNoteChanged()
|
||||
mNoteChanged = true
|
||||
noteChanged = true
|
||||
setOkResult()
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
internal fun changePreviewedCard(nextCard: Boolean) {
|
||||
mIndex = if (nextCard) mIndex + 1 else mIndex - 1
|
||||
currentCard = getColUnsafe.getCard(mCardList[mIndex])
|
||||
index = if (nextCard) index + 1 else index - 1
|
||||
currentCard = getColUnsafe.getCard(cardList[index])
|
||||
displayCardQuestion()
|
||||
updateProgress()
|
||||
}
|
||||
|
||||
private val mToggleAnswerHandler = View.OnClickListener {
|
||||
if (mShowingAnswer) {
|
||||
private val toggleAnswerHandler = View.OnClickListener {
|
||||
if (showingAnswer) {
|
||||
displayCardQuestion()
|
||||
} else {
|
||||
displayCardAnswer()
|
||||
@ -245,32 +245,32 @@ class Previewer : AbstractFlashcardViewer() {
|
||||
}
|
||||
|
||||
private fun updateButtonsState() {
|
||||
previewLayout!!.setShowingAnswer(mShowingAnswer)
|
||||
previewLayout!!.setShowingAnswer(showingAnswer)
|
||||
|
||||
// If we are in single-card mode, we show the "Show Answer" button on the question side
|
||||
// and hide navigation buttons.
|
||||
if (mCardList.size == 1) {
|
||||
if (cardList.size == 1) {
|
||||
previewLayout!!.hideNavigationButtons()
|
||||
return
|
||||
}
|
||||
previewLayout!!.setPrevButtonEnabled(mIndex > 0)
|
||||
previewLayout!!.setNextButtonEnabled(mIndex < mCardList.size - 1)
|
||||
previewLayout!!.setPrevButtonEnabled(index > 0)
|
||||
previewLayout!!.setNextButtonEnabled(index < cardList.size - 1)
|
||||
}
|
||||
|
||||
private fun updateProgress() {
|
||||
if (mProgressSeekBar.progress != mIndex) {
|
||||
mProgressSeekBar.progress = mIndex
|
||||
if (progressSeekBar.progress != index) {
|
||||
progressSeekBar.progress = index
|
||||
}
|
||||
val progress = getString(R.string.preview_progress_bar_text, mIndex + 1, mCardList.size)
|
||||
mProgressText.text = progress
|
||||
val progress = getString(R.string.preview_progress_bar_text, index + 1, cardList.size)
|
||||
progressText.text = progress
|
||||
}
|
||||
|
||||
private fun setOkResult() {
|
||||
setResult(
|
||||
RESULT_OK,
|
||||
Intent().apply {
|
||||
putExtra("reloadRequired", mReloadRequired)
|
||||
putExtra("noteChanged", mNoteChanged)
|
||||
putExtra("reloadRequired", reloadRequired)
|
||||
putExtra("noteChanged", noteChanged)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -105,10 +105,10 @@ open class Reviewer :
|
||||
PostRequestHandler {
|
||||
private var queueState: CurrentQueueState? = null
|
||||
private val customSchedulingKey = TimeManager.time.intTimeMS().toString()
|
||||
private var mHasDrawerSwipeConflicts = false
|
||||
private var mShowWhiteboard = true
|
||||
private var mPrefFullscreenReview = false
|
||||
private lateinit var mColorPalette: LinearLayout
|
||||
private var hasDrawerSwipeConflicts = false
|
||||
private var showWhiteboard = true
|
||||
private var prefFullscreenReview = false
|
||||
private lateinit var colorPalette: LinearLayout
|
||||
private var toggleStylus = false
|
||||
|
||||
private val server = AnkiServer(this).also { it.start() }
|
||||
@ -134,14 +134,14 @@ open class Reviewer :
|
||||
|
||||
// TODO: Consider extracting to ViewModel
|
||||
// Card counts
|
||||
private var mNewCount: SpannableString? = null
|
||||
private var mLrnCount: SpannableString? = null
|
||||
private var mRevCount: SpannableString? = null
|
||||
private lateinit var mTextBarNew: TextView
|
||||
private lateinit var mTextBarLearn: TextView
|
||||
private lateinit var mTextBarReview: TextView
|
||||
private var newCount: SpannableString? = null
|
||||
private var lrnCount: SpannableString? = null
|
||||
private var revCount: SpannableString? = null
|
||||
private lateinit var textBarNew: TextView
|
||||
private lateinit var textBarLearn: TextView
|
||||
private lateinit var textBarReview: TextView
|
||||
private lateinit var answerTimer: AnswerTimer
|
||||
private var mPrefHideDueCount = false
|
||||
private var prefHideDueCount = false
|
||||
|
||||
// Whiteboard
|
||||
var prefWhiteboard = false
|
||||
@ -158,21 +158,21 @@ open class Reviewer :
|
||||
private lateinit var micToolBarLayer: LinearLayout
|
||||
|
||||
// ETA
|
||||
private var mEta = 0
|
||||
private var eta = 0
|
||||
|
||||
/** Handle Mark/Flag state of cards */
|
||||
@VisibleForTesting
|
||||
internal var mCardMarker: CardMarker? = null
|
||||
internal var cardMarker: CardMarker? = null
|
||||
|
||||
// Preferences from the collection
|
||||
private var mShowRemainingCardCount = false
|
||||
private var showRemainingCardCount = false
|
||||
private var stopTimerOnAnswer = false
|
||||
private val mActionButtons = ActionButtons()
|
||||
private lateinit var mToolbar: Toolbar
|
||||
private val actionButtons = ActionButtons()
|
||||
private lateinit var toolbar: Toolbar
|
||||
|
||||
@VisibleForTesting
|
||||
protected val mProcessor = PeripheralKeymap(this, this)
|
||||
private val mOnboarding = Onboarding.Reviewer(this)
|
||||
protected val processor = PeripheralKeymap(this, this)
|
||||
private val onboarding = Onboarding.Reviewer(this)
|
||||
|
||||
private val addNoteLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
@ -189,12 +189,12 @@ open class Reviewer :
|
||||
finish()
|
||||
return
|
||||
}
|
||||
mColorPalette = findViewById(R.id.whiteboard_editor)
|
||||
colorPalette = findViewById(R.id.whiteboard_editor)
|
||||
answerTimer = AnswerTimer(findViewById(R.id.card_time))
|
||||
mTextBarNew = findViewById(R.id.new_number)
|
||||
mTextBarLearn = findViewById(R.id.learn_number)
|
||||
mTextBarReview = findViewById(R.id.review_number)
|
||||
mToolbar = findViewById(R.id.toolbar)
|
||||
textBarNew = findViewById(R.id.new_number)
|
||||
textBarLearn = findViewById(R.id.learn_number)
|
||||
textBarReview = findViewById(R.id.review_number)
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
micToolBarLayer = findViewById(R.id.mic_tool_bar_layer)
|
||||
|
||||
startLoadingCollection()
|
||||
@ -225,8 +225,8 @@ open class Reviewer :
|
||||
get() {
|
||||
return FlagToDisplay(
|
||||
currentCard!!.userFlag(),
|
||||
mActionButtons.findMenuItem(ActionButtons.RES_FLAG)?.isActionButton ?: true,
|
||||
mPrefFullscreenReview
|
||||
actionButtons.findMenuItem(ActionButtons.RES_FLAG)?.isActionButton ?: true,
|
||||
prefFullscreenReview
|
||||
).get()
|
||||
}
|
||||
|
||||
@ -245,8 +245,8 @@ open class Reviewer :
|
||||
}
|
||||
|
||||
// If we don't know: assume it's not shown
|
||||
val shownAsToolbarButton = mActionButtons.findMenuItem(ActionButtons.RES_MARK)?.isActionButton == true
|
||||
return !shownAsToolbarButton || mPrefFullscreenReview
|
||||
val shownAsToolbarButton = actionButtons.findMenuItem(ActionButtons.RES_MARK)?.isActionButton == true
|
||||
return !shownAsToolbarButton || prefFullscreenReview
|
||||
}
|
||||
|
||||
protected open fun onMark(card: Card?) {
|
||||
@ -264,7 +264,7 @@ open class Reviewer :
|
||||
if (currentCard == null) {
|
||||
return
|
||||
}
|
||||
mCardMarker!!.displayMark(shouldDisplayMark())
|
||||
cardMarker!!.displayMark(shouldDisplayMark())
|
||||
}
|
||||
|
||||
protected open fun onFlag(card: Card?, flag: Flag) {
|
||||
@ -285,7 +285,7 @@ open class Reviewer :
|
||||
if (currentCard == null) {
|
||||
return
|
||||
}
|
||||
mCardMarker!!.displayFlag(Flag.fromCode(flagToDisplay))
|
||||
cardMarker!!.displayFlag(Flag.fromCode(flagToDisplay))
|
||||
}
|
||||
|
||||
private fun selectDeckFromExtra() {
|
||||
@ -343,7 +343,7 @@ open class Reviewer :
|
||||
disableDrawerSwipeOnConflicts()
|
||||
|
||||
// Set full screen/immersive mode if needed
|
||||
if (mPrefFullscreenReview) {
|
||||
if (prefFullscreenReview) {
|
||||
setFullScreen(this)
|
||||
}
|
||||
setRenderWorkaround(this)
|
||||
@ -366,7 +366,7 @@ open class Reviewer :
|
||||
}
|
||||
R.id.action_undo -> {
|
||||
Timber.i("Reviewer:: Undo button pressed")
|
||||
if (mShowWhiteboard && whiteboard != null && !whiteboard!!.undoEmpty()) {
|
||||
if (showWhiteboard && whiteboard != null && !whiteboard!!.undoEmpty()) {
|
||||
whiteboard!!.undo()
|
||||
} else {
|
||||
undo()
|
||||
@ -432,8 +432,8 @@ open class Reviewer :
|
||||
clearWhiteboard()
|
||||
}
|
||||
R.id.action_hide_whiteboard -> { // toggle whiteboard visibility
|
||||
Timber.i("Reviewer:: Whiteboard visibility set to %b", !mShowWhiteboard)
|
||||
setWhiteboardVisibility(!mShowWhiteboard)
|
||||
Timber.i("Reviewer:: Whiteboard visibility set to %b", !showWhiteboard)
|
||||
setWhiteboardVisibility(!showWhiteboard)
|
||||
refreshActionBar()
|
||||
}
|
||||
R.id.action_toggle_stylus -> { // toggle stylus mode
|
||||
@ -509,7 +509,7 @@ open class Reviewer :
|
||||
setWhiteboardEnabledState(prefWhiteboard)
|
||||
setWhiteboardVisibility(prefWhiteboard)
|
||||
if (!prefWhiteboard) {
|
||||
mColorPalette.visibility = View.GONE
|
||||
colorPalette.visibility = View.GONE
|
||||
}
|
||||
refreshActionBar()
|
||||
}
|
||||
@ -521,10 +521,10 @@ open class Reviewer :
|
||||
}
|
||||
|
||||
public override fun changeWhiteboardPenColor() {
|
||||
if (mColorPalette.visibility == View.GONE) {
|
||||
mColorPalette.visibility = View.VISIBLE
|
||||
if (colorPalette.visibility == View.GONE) {
|
||||
colorPalette.visibility = View.VISIBLE
|
||||
} else {
|
||||
mColorPalette.visibility = View.GONE
|
||||
colorPalette.visibility = View.GONE
|
||||
}
|
||||
updateWhiteboardEditorPosition()
|
||||
}
|
||||
@ -704,7 +704,7 @@ open class Reviewer :
|
||||
// NOTE: This is called every time a new question is shown via invalidate options menu
|
||||
menuInflater.inflate(R.menu.reviewer, menu)
|
||||
displayIcons(menu)
|
||||
mActionButtons.setCustomButtonsStatus(menu)
|
||||
actionButtons.setCustomButtonsStatus(menu)
|
||||
val alpha = Themes.ALPHA_ICON_ENABLED_LIGHT
|
||||
val markCardIcon = menu.findItem(R.id.action_mark_card)
|
||||
if (currentCard != null && isMarked(currentCard!!.note())) {
|
||||
@ -734,7 +734,7 @@ open class Reviewer :
|
||||
// Undo button
|
||||
@DrawableRes val undoIconId: Int
|
||||
val undoEnabled: Boolean
|
||||
val whiteboardIsShownAndHasStrokes = mShowWhiteboard && whiteboard?.undoEmpty() == false
|
||||
val whiteboardIsShownAndHasStrokes = showWhiteboard && whiteboard?.undoEmpty() == false
|
||||
if (whiteboardIsShownAndHasStrokes) {
|
||||
undoIconId = R.drawable.eraser
|
||||
undoEnabled = true
|
||||
@ -772,7 +772,7 @@ open class Reviewer :
|
||||
}
|
||||
}
|
||||
if (undoEnabled) {
|
||||
mOnboarding.onUndoButtonEnabled()
|
||||
onboarding.onUndoButtonEnabled()
|
||||
}
|
||||
val toggleWhiteboardIcon = menu.findItem(R.id.action_toggle_whiteboard)
|
||||
val toggleStylusIcon = menu.findItem(R.id.action_toggle_stylus)
|
||||
@ -784,25 +784,25 @@ open class Reviewer :
|
||||
toggleWhiteboardIcon.setTitle(R.string.disable_whiteboard)
|
||||
// Always allow "Disable Whiteboard", even if "Enable Whiteboard" is disabled
|
||||
toggleWhiteboardIcon.isVisible = true
|
||||
if (!mActionButtons.status.toggleStylusIsDisabled()) {
|
||||
if (!actionButtons.status.toggleStylusIsDisabled()) {
|
||||
toggleStylusIcon.isVisible = true
|
||||
}
|
||||
if (!mActionButtons.status.hideWhiteboardIsDisabled()) {
|
||||
if (!actionButtons.status.hideWhiteboardIsDisabled()) {
|
||||
hideWhiteboardIcon.isVisible = true
|
||||
}
|
||||
if (!mActionButtons.status.clearWhiteboardIsDisabled()) {
|
||||
if (!actionButtons.status.clearWhiteboardIsDisabled()) {
|
||||
menu.findItem(R.id.action_clear_whiteboard).isVisible = true
|
||||
}
|
||||
if (!mActionButtons.status.saveWhiteboardIsDisabled()) {
|
||||
if (!actionButtons.status.saveWhiteboardIsDisabled()) {
|
||||
menu.findItem(R.id.action_save_whiteboard).isVisible = true
|
||||
}
|
||||
if (!mActionButtons.status.whiteboardPenColorIsDisabled()) {
|
||||
if (!actionButtons.status.whiteboardPenColorIsDisabled()) {
|
||||
changePenColorIcon.isVisible = true
|
||||
}
|
||||
val whiteboardIcon = ContextCompat.getDrawable(this, R.drawable.ic_gesture_white)!!.mutate()
|
||||
val stylusIcon = ContextCompat.getDrawable(this, R.drawable.ic_gesture_stylus)!!.mutate()
|
||||
val whiteboardColorPaletteIcon = VectorDrawableCompat.create(resources, R.drawable.ic_color_lens_white_24dp, this.theme)!!.mutate()
|
||||
if (mShowWhiteboard) {
|
||||
if (showWhiteboard) {
|
||||
whiteboardIcon.alpha = Themes.ALPHA_ICON_ENABLED_LIGHT
|
||||
hideWhiteboardIcon.icon = whiteboardIcon
|
||||
hideWhiteboardIcon.setTitle(R.string.hide_whiteboard)
|
||||
@ -826,7 +826,7 @@ open class Reviewer :
|
||||
toggleStylusIcon.icon = stylusIcon
|
||||
changePenColorIcon.isEnabled = false
|
||||
changePenColorIcon.icon = whiteboardColorPaletteIcon
|
||||
mColorPalette.visibility = View.GONE
|
||||
colorPalette.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
toggleWhiteboardIcon.setTitle(R.string.enable_whiteboard)
|
||||
@ -834,19 +834,19 @@ open class Reviewer :
|
||||
if (colIsOpenUnsafe() && getColUnsafe.decks.isDyn(parentDid)) {
|
||||
menu.findItem(R.id.action_open_deck_options).isVisible = false
|
||||
}
|
||||
if (mTTS.enabled && !mActionButtons.status.selectTtsIsDisabled()) {
|
||||
if (tts.enabled && !actionButtons.status.selectTtsIsDisabled()) {
|
||||
menu.findItem(R.id.action_select_tts).isVisible = true
|
||||
}
|
||||
if (!suspendNoteAvailable() && !mActionButtons.status.suspendIsDisabled()) {
|
||||
if (!suspendNoteAvailable() && !actionButtons.status.suspendIsDisabled()) {
|
||||
menu.findItem(R.id.action_suspend).isVisible = false
|
||||
menu.findItem(R.id.action_suspend_card).isVisible = true
|
||||
}
|
||||
if (!buryNoteAvailable() && !mActionButtons.status.buryIsDisabled()) {
|
||||
if (!buryNoteAvailable() && !actionButtons.status.buryIsDisabled()) {
|
||||
menu.findItem(R.id.action_bury).isVisible = false
|
||||
menu.findItem(R.id.action_bury_card).isVisible = true
|
||||
}
|
||||
|
||||
mOnboarding.onCreate()
|
||||
onboarding.onCreate()
|
||||
|
||||
increaseHorizontalPaddingOfOverflowMenuIcons(menu)
|
||||
tintOverflowMenuIcons(menu, skipIf = { isFlagResource(it.itemId) })
|
||||
@ -875,14 +875,14 @@ open class Reviewer :
|
||||
if (answerFieldIsFocused()) {
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
if (mProcessor.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)) {
|
||||
if (processor.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
||||
return if (mProcessor.onKeyUp(keyCode, event)) {
|
||||
return if (processor.onKeyUp(keyCode, event)) {
|
||||
true
|
||||
} else {
|
||||
super.onKeyUp(keyCode, event)
|
||||
@ -906,7 +906,7 @@ open class Reviewer :
|
||||
|
||||
override fun displayAnswerBottomBar() {
|
||||
super.displayAnswerBottomBar()
|
||||
mOnboarding.onAnswerShown()
|
||||
onboarding.onAnswerShown()
|
||||
// Set correct label and background resource for each button
|
||||
// Note that it's necessary to set the resource dynamically as the ease2 / ease3 buttons
|
||||
// (which libanki expects ease to be 2 and 3) can either be hard, good, or easy - depending on num buttons shown
|
||||
@ -945,10 +945,10 @@ open class Reviewer :
|
||||
|
||||
override fun restorePreferences(): SharedPreferences {
|
||||
val preferences = super.restorePreferences()
|
||||
mPrefHideDueCount = preferences.getBoolean("hideDueCount", false)
|
||||
mProcessor.setup()
|
||||
mPrefFullscreenReview = isFullScreenReview(preferences)
|
||||
mActionButtons.setup(preferences)
|
||||
prefHideDueCount = preferences.getBoolean("hideDueCount", false)
|
||||
processor.setup()
|
||||
prefFullscreenReview = isFullScreenReview(preferences)
|
||||
actionButtons.setup(preferences)
|
||||
return preferences
|
||||
}
|
||||
|
||||
@ -958,22 +958,22 @@ open class Reviewer :
|
||||
}
|
||||
|
||||
private fun updateWhiteboardEditorPosition() {
|
||||
mAnswerButtonsPosition = this.sharedPrefs()
|
||||
answerButtonsPosition = this.sharedPrefs()
|
||||
.getString("answerButtonPosition", "bottom")
|
||||
val layoutParams: RelativeLayout.LayoutParams
|
||||
when (mAnswerButtonsPosition) {
|
||||
when (answerButtonsPosition) {
|
||||
"none", "top" -> {
|
||||
layoutParams = mColorPalette.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams = colorPalette.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams.removeRule(RelativeLayout.ABOVE)
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
|
||||
mColorPalette.layoutParams = layoutParams
|
||||
colorPalette.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
"bottom" -> {
|
||||
layoutParams = mColorPalette.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams = colorPalette.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
|
||||
layoutParams.addRule(RelativeLayout.ABOVE, R.id.bottom_area_layout)
|
||||
mColorPalette.layoutParams = layoutParams
|
||||
colorPalette.layoutParams = layoutParams
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -982,29 +982,29 @@ open class Reviewer :
|
||||
val queue = queueState ?: return
|
||||
super.updateActionBar()
|
||||
val counts = queue.counts
|
||||
mNewCount = SpannableString(counts.new.toString())
|
||||
mLrnCount = SpannableString(counts.lrn.toString())
|
||||
mRevCount = SpannableString(counts.rev.toString())
|
||||
if (mPrefHideDueCount) {
|
||||
mRevCount = SpannableString("???")
|
||||
newCount = SpannableString(counts.new.toString())
|
||||
lrnCount = SpannableString(counts.lrn.toString())
|
||||
revCount = SpannableString(counts.rev.toString())
|
||||
if (prefHideDueCount) {
|
||||
revCount = SpannableString("???")
|
||||
}
|
||||
// if this code is run as a card is being answered, currentCard may be non-null but
|
||||
// the queues may be empty - we can't call countIdx() in such a case
|
||||
if (counts.count() != 0) {
|
||||
when (queue.countsIndex) {
|
||||
Counts.Queue.NEW -> mNewCount!!.setSpan(UnderlineSpan(), 0, mNewCount!!.length, 0)
|
||||
Counts.Queue.LRN -> mLrnCount!!.setSpan(UnderlineSpan(), 0, mLrnCount!!.length, 0)
|
||||
Counts.Queue.REV -> mRevCount!!.setSpan(UnderlineSpan(), 0, mRevCount!!.length, 0)
|
||||
Counts.Queue.NEW -> newCount!!.setSpan(UnderlineSpan(), 0, newCount!!.length, 0)
|
||||
Counts.Queue.LRN -> lrnCount!!.setSpan(UnderlineSpan(), 0, lrnCount!!.length, 0)
|
||||
Counts.Queue.REV -> revCount!!.setSpan(UnderlineSpan(), 0, revCount!!.length, 0)
|
||||
}
|
||||
}
|
||||
mTextBarNew.text = mNewCount
|
||||
mTextBarLearn.text = mLrnCount
|
||||
mTextBarReview.text = mRevCount
|
||||
textBarNew.text = newCount
|
||||
textBarLearn.text = lrnCount
|
||||
textBarReview.text = revCount
|
||||
}
|
||||
|
||||
override fun fillFlashcard() {
|
||||
super.fillFlashcard()
|
||||
if (!isDisplayingAnswer && mShowWhiteboard && whiteboard != null) {
|
||||
if (!isDisplayingAnswer && showWhiteboard && whiteboard != null) {
|
||||
whiteboard!!.clear()
|
||||
}
|
||||
}
|
||||
@ -1121,25 +1121,25 @@ open class Reviewer :
|
||||
|
||||
override fun initLayout() {
|
||||
super.initLayout()
|
||||
if (!mShowRemainingCardCount) {
|
||||
mTextBarNew.visibility = View.GONE
|
||||
mTextBarLearn.visibility = View.GONE
|
||||
mTextBarReview.visibility = View.GONE
|
||||
if (!showRemainingCardCount) {
|
||||
textBarNew.visibility = View.GONE
|
||||
textBarLearn.visibility = View.GONE
|
||||
textBarReview.visibility = View.GONE
|
||||
}
|
||||
|
||||
// can't move this into onCreate due to mTopBarLayout
|
||||
val mark = topBarLayout!!.findViewById<ImageView>(R.id.mark_icon)
|
||||
val flag = topBarLayout!!.findViewById<ImageView>(R.id.flag_icon)
|
||||
mCardMarker = CardMarker(mark, flag)
|
||||
cardMarker = CardMarker(mark, flag)
|
||||
}
|
||||
|
||||
override fun switchTopBarVisibility(visible: Int) {
|
||||
super.switchTopBarVisibility(visible)
|
||||
answerTimer.setVisibility(visible)
|
||||
if (mShowRemainingCardCount) {
|
||||
mTextBarNew.visibility = visible
|
||||
mTextBarLearn.visibility = visible
|
||||
mTextBarReview.visibility = visible
|
||||
if (showRemainingCardCount) {
|
||||
textBarNew.visibility = visible
|
||||
textBarLearn.visibility = visible
|
||||
textBarReview.visibility = visible
|
||||
}
|
||||
}
|
||||
|
||||
@ -1153,12 +1153,12 @@ open class Reviewer :
|
||||
override fun initControls() {
|
||||
super.initControls()
|
||||
if (prefWhiteboard) {
|
||||
setWhiteboardVisibility(mShowWhiteboard)
|
||||
setWhiteboardVisibility(showWhiteboard)
|
||||
}
|
||||
if (mShowRemainingCardCount) {
|
||||
mTextBarNew.visibility = View.VISIBLE
|
||||
mTextBarLearn.visibility = View.VISIBLE
|
||||
mTextBarReview.visibility = View.VISIBLE
|
||||
if (showRemainingCardCount) {
|
||||
textBarNew.visibility = View.VISIBLE
|
||||
textBarLearn.visibility = View.VISIBLE
|
||||
textBarReview.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
@ -1232,12 +1232,12 @@ open class Reviewer :
|
||||
|
||||
override fun restoreCollectionPreferences(col: Collection) {
|
||||
super.restoreCollectionPreferences(col)
|
||||
mShowRemainingCardCount = col.config.get("dueCounts") ?: true
|
||||
showRemainingCardCount = col.config.get("dueCounts") ?: true
|
||||
stopTimerOnAnswer = col.decks.confForDid(col.decks.current().id).getBoolean("stopTimerOnAnswer")
|
||||
}
|
||||
|
||||
override fun onSingleTap(): Boolean {
|
||||
if (mPrefFullscreenReview && isImmersiveSystemUiVisible(this)) {
|
||||
if (prefFullscreenReview && isImmersiveSystemUiVisible(this)) {
|
||||
delayedHide(INITIAL_HIDE_DELAY)
|
||||
return true
|
||||
}
|
||||
@ -1245,7 +1245,7 @@ open class Reviewer :
|
||||
}
|
||||
|
||||
override fun onFling() {
|
||||
if (mPrefFullscreenReview && isImmersiveSystemUiVisible(this)) {
|
||||
if (prefFullscreenReview && isImmersiveSystemUiVisible(this)) {
|
||||
delayedHide(INITIAL_HIDE_DELAY)
|
||||
}
|
||||
}
|
||||
@ -1262,9 +1262,9 @@ open class Reviewer :
|
||||
}
|
||||
}
|
||||
|
||||
private val mFullScreenHandler: Handler = object : Handler(getDefaultLooper()) {
|
||||
private val fullScreenHandler: Handler = object : Handler(getDefaultLooper()) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
if (mPrefFullscreenReview) {
|
||||
if (prefFullscreenReview) {
|
||||
setFullScreen(this@Reviewer)
|
||||
}
|
||||
}
|
||||
@ -1273,8 +1273,8 @@ open class Reviewer :
|
||||
/** Hide the navigation if in full-screen mode after a given period of time */
|
||||
protected open fun delayedHide(delayMillis: Int) {
|
||||
Timber.d("Fullscreen delayed hide in %dms", delayMillis)
|
||||
mFullScreenHandler.removeMessages(0)
|
||||
mFullScreenHandler.sendEmptyMessageDelayed(0, delayMillis.toLong())
|
||||
fullScreenHandler.removeMessages(0)
|
||||
fullScreenHandler.sendEmptyMessageDelayed(0, delayMillis.toLong())
|
||||
}
|
||||
|
||||
private fun setWhiteboardEnabledState(state: Boolean) {
|
||||
@ -1409,8 +1409,8 @@ open class Reviewer :
|
||||
if (event == null) return@setOnTouchListener false
|
||||
// If the whiteboard is currently drawing, and triggers the system UI to show, we want to continue drawing.
|
||||
if (!whiteboard!!.isCurrentlyDrawing && (
|
||||
!mShowWhiteboard || (
|
||||
mPrefFullscreenReview &&
|
||||
!showWhiteboard || (
|
||||
prefFullscreenReview &&
|
||||
isImmersiveSystemUiVisible(this@Reviewer)
|
||||
)
|
||||
)
|
||||
@ -1425,22 +1425,22 @@ open class Reviewer :
|
||||
|
||||
// Show or hide the whiteboard
|
||||
private fun setWhiteboardVisibility(state: Boolean) {
|
||||
mShowWhiteboard = state
|
||||
showWhiteboard = state
|
||||
MetaDB.storeWhiteboardVisibility(this, parentDid, state)
|
||||
if (state) {
|
||||
whiteboard!!.visibility = View.VISIBLE
|
||||
disableDrawerSwipe()
|
||||
} else {
|
||||
whiteboard!!.visibility = View.GONE
|
||||
if (!mHasDrawerSwipeConflicts) {
|
||||
if (!hasDrawerSwipeConflicts) {
|
||||
enableDrawerSwipe()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableDrawerSwipeOnConflicts() {
|
||||
if (mGestureProcessor.isBound(Gesture.SWIPE_UP, Gesture.SWIPE_DOWN, Gesture.SWIPE_RIGHT)) {
|
||||
mHasDrawerSwipeConflicts = true
|
||||
if (gestureProcessor.isBound(Gesture.SWIPE_UP, Gesture.SWIPE_DOWN, Gesture.SWIPE_RIGHT)) {
|
||||
hasDrawerSwipeConflicts = true
|
||||
super.disableDrawerSwipe()
|
||||
}
|
||||
}
|
||||
@ -1454,7 +1454,7 @@ open class Reviewer :
|
||||
if (hasFocus) {
|
||||
delayedHide(INITIAL_HIDE_DELAY)
|
||||
} else {
|
||||
mFullScreenHandler.removeMessages(0)
|
||||
fullScreenHandler.removeMessages(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1492,19 +1492,19 @@ open class Reviewer :
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun hasDrawerSwipeConflicts(): Boolean {
|
||||
return mHasDrawerSwipeConflicts
|
||||
return hasDrawerSwipeConflicts
|
||||
}
|
||||
|
||||
override fun getCardDataForJsApi(): AnkiDroidJsAPI.CardDataForJsApi {
|
||||
val cardDataForJsAPI = AnkiDroidJsAPI.CardDataForJsApi().apply {
|
||||
newCardCount = mNewCount.toString()
|
||||
lrnCardCount = mLrnCount.toString()
|
||||
revCardCount = mRevCount.toString()
|
||||
newCardCount = newCount.toString()
|
||||
lrnCardCount = lrnCount.toString()
|
||||
revCardCount = revCount.toString()
|
||||
nextTime1 = easeButton1!!.nextTime
|
||||
nextTime2 = easeButton2!!.nextTime
|
||||
nextTime3 = easeButton3!!.nextTime
|
||||
nextTime4 = easeButton4!!.nextTime
|
||||
eta = mEta
|
||||
eta = this@Reviewer.eta
|
||||
}
|
||||
return cardDataForJsAPI
|
||||
}
|
||||
|
@ -52,19 +52,19 @@ import kotlin.math.abs
|
||||
*/
|
||||
class SharedDecksDownloadFragment : Fragment() {
|
||||
|
||||
private var mDownloadId: Long = 0
|
||||
private var downloadId: Long = 0
|
||||
|
||||
private var mFileName: String? = null
|
||||
private var fileName: String? = null
|
||||
|
||||
private var mHandler: Handler = Handler(Looper.getMainLooper())
|
||||
private var mIsProgressCheckerRunning = false
|
||||
private var handler: Handler = Handler(Looper.getMainLooper())
|
||||
private var isProgressCheckerRunning = false
|
||||
|
||||
private lateinit var mCancelButton: Button
|
||||
private lateinit var mTryAgainButton: Button
|
||||
private lateinit var mImportDeckButton: Button
|
||||
private lateinit var mDownloadPercentageText: TextView
|
||||
private lateinit var mDownloadProgressBar: ProgressBar
|
||||
private lateinit var mCheckNetworkInfoText: TextView
|
||||
private lateinit var cancelButton: Button
|
||||
private lateinit var tryAgainButton: Button
|
||||
private lateinit var importDeckButton: Button
|
||||
private lateinit var downloadPercentageText: TextView
|
||||
private lateinit var downloadProgressBar: ProgressBar
|
||||
private lateinit var checkNetworkInfoText: TextView
|
||||
|
||||
/**
|
||||
* Android's DownloadManager - Used here to manage the functionality of downloading decks, one
|
||||
@ -73,7 +73,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
* Since only one download is supported at a time, the DownloadManager's queue is expected to
|
||||
* have a single request at a time.
|
||||
*/
|
||||
private lateinit var mDownloadManager: DownloadManager
|
||||
private lateinit var downloadManager: DownloadManager
|
||||
|
||||
var isDownloadInProgress = false
|
||||
|
||||
@ -108,34 +108,34 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mDownloadPercentageText = view.findViewById(R.id.download_percentage)
|
||||
mDownloadProgressBar = view.findViewById(R.id.download_progress)
|
||||
mCancelButton = view.findViewById(R.id.cancel_shared_decks_download)
|
||||
mImportDeckButton = view.findViewById(R.id.import_shared_deck_button)
|
||||
mTryAgainButton = view.findViewById(R.id.try_again_deck_download)
|
||||
mCheckNetworkInfoText = view.findViewById(R.id.check_network_info_text)
|
||||
downloadPercentageText = view.findViewById(R.id.download_percentage)
|
||||
downloadProgressBar = view.findViewById(R.id.download_progress)
|
||||
cancelButton = view.findViewById(R.id.cancel_shared_decks_download)
|
||||
importDeckButton = view.findViewById(R.id.import_shared_deck_button)
|
||||
tryAgainButton = view.findViewById(R.id.try_again_deck_download)
|
||||
checkNetworkInfoText = view.findViewById(R.id.check_network_info_text)
|
||||
|
||||
val fileToBeDownloaded = arguments?.getSerializableCompat<DownloadFile>(DOWNLOAD_FILE)!!
|
||||
mDownloadManager = (activity as SharedDecksActivity).downloadManager
|
||||
downloadManager = (activity as SharedDecksActivity).downloadManager
|
||||
|
||||
downloadFile(fileToBeDownloaded)
|
||||
|
||||
mCancelButton.setOnClickListener {
|
||||
cancelButton.setOnClickListener {
|
||||
Timber.i("Cancel download button clicked which would lead to showing of confirmation dialog")
|
||||
showCancelConfirmationDialog()
|
||||
}
|
||||
|
||||
mImportDeckButton.setOnClickListener {
|
||||
importDeckButton.setOnClickListener {
|
||||
Timber.i("Import deck button clicked")
|
||||
openDownloadedDeck(context)
|
||||
}
|
||||
|
||||
mTryAgainButton.setOnClickListener {
|
||||
tryAgainButton.setOnClickListener {
|
||||
Timber.i("Try again button clicked, retry downloading of deck")
|
||||
mDownloadManager.remove(mDownloadId)
|
||||
downloadManager.remove(downloadId)
|
||||
downloadFile(fileToBeDownloaded)
|
||||
mCancelButton.visibility = View.VISIBLE
|
||||
mTryAgainButton.visibility = View.GONE
|
||||
cancelButton.visibility = View.VISIBLE
|
||||
tryAgainButton.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
}
|
||||
// Register broadcast receiver for download completion.
|
||||
Timber.d("Registering broadcast receiver for download completion")
|
||||
activity?.registerReceiver(mOnComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
activity?.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
|
||||
val currentFileName = URLUtil.guessFileName(
|
||||
fileToBeDownloaded.url,
|
||||
@ -170,12 +170,12 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
val downloadRequest = generateDeckDownloadRequest(fileToBeDownloaded, currentFileName)
|
||||
|
||||
// Store unique download ID to be used when onReceive() of BroadcastReceiver gets executed.
|
||||
mDownloadId = mDownloadManager.enqueue(downloadRequest)
|
||||
mFileName = currentFileName
|
||||
downloadId = downloadManager.enqueue(downloadRequest)
|
||||
fileName = currentFileName
|
||||
isDownloadInProgress = true
|
||||
Timber.d("Download ID -> $mDownloadId")
|
||||
Timber.d("File name -> $mFileName")
|
||||
view?.findViewById<TextView>(R.id.downloading_title)?.text = getString(R.string.downloading_file, mFileName)
|
||||
Timber.d("Download ID -> $downloadId")
|
||||
Timber.d("File name -> $fileName")
|
||||
view?.findViewById<TextView>(R.id.downloading_title)?.text = getString(R.string.downloading_file, fileName)
|
||||
startDownloadProgressChecker()
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
* Registered in downloadFile() method.
|
||||
* When onReceive() is called, open the deck file in AnkiDroid to import it.
|
||||
*/
|
||||
private var mOnComplete: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
private var onComplete: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
Timber.i("Download might be complete now, verify and continue with import")
|
||||
|
||||
@ -212,7 +212,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
* @return Whether the data in the received data is an importable deck
|
||||
*/
|
||||
fun verifyDeckIsImportable(): Boolean {
|
||||
if (mFileName == null) {
|
||||
if (fileName == null) {
|
||||
// Send ACRA report
|
||||
CrashReportService.sendExceptionReport(
|
||||
"File name is null",
|
||||
@ -222,7 +222,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
}
|
||||
|
||||
// Return if mDownloadId does not match with the ID of the completed download.
|
||||
if (mDownloadId != intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)) {
|
||||
if (downloadId != intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)) {
|
||||
Timber.w(
|
||||
"Download ID did not match with the ID of the completed download. " +
|
||||
"Download completion related to some other download might have been received. " +
|
||||
@ -234,15 +234,15 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
stopDownloadProgressChecker()
|
||||
|
||||
// Halt execution if file doesn't have extension as 'apkg' or 'colpkg'
|
||||
if (!ImportUtils.isFileAValidDeck(mFileName!!)) {
|
||||
if (!ImportUtils.isFileAValidDeck(fileName!!)) {
|
||||
Timber.i("File does not have 'apkg' or 'colpkg' extension, abort the deck opening task")
|
||||
checkDownloadStatusAndUnregisterReceiver(isSuccessful = false, isInvalidDeckFile = true)
|
||||
return false
|
||||
}
|
||||
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterById(mDownloadId)
|
||||
val cursor = mDownloadManager.query(query)
|
||||
query.setFilterById(downloadId)
|
||||
val cursor = downloadManager.query(query)
|
||||
|
||||
cursor.use {
|
||||
// Return if cursor is empty.
|
||||
@ -282,12 +282,12 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
|
||||
if (isVisible) {
|
||||
// Setting these since progress checker can stop before progress is updated to represent 100%
|
||||
mDownloadPercentageText.text = getString(R.string.percentage, DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE)
|
||||
mDownloadProgressBar.progress = DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE.toInt()
|
||||
downloadPercentageText.text = getString(R.string.percentage, DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE)
|
||||
downloadProgressBar.progress = DOWNLOAD_COMPLETED_PROGRESS_PERCENTAGE.toInt()
|
||||
|
||||
// Remove cancel button and show import deck button
|
||||
mCancelButton.visibility = View.GONE
|
||||
mImportDeckButton.visibility = View.VISIBLE
|
||||
cancelButton.visibility = View.GONE
|
||||
importDeckButton.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
Timber.i("Opening downloaded deck for import")
|
||||
@ -304,7 +304,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
private fun unregisterReceiver() {
|
||||
Timber.d("Unregistering receiver")
|
||||
try {
|
||||
activity?.unregisterReceiver(mOnComplete)
|
||||
activity?.unregisterReceiver(onComplete)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
// This might throw an exception in cases where the receiver is already in unregistered state.
|
||||
// Log the exception in such cases, there is nothing else to do.
|
||||
@ -316,7 +316,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
/**
|
||||
* Check download progress and update status at intervals of 0.1 second.
|
||||
*/
|
||||
private val mDownloadProgressChecker: Runnable by lazy {
|
||||
private val downloadProgressChecker: Runnable by lazy {
|
||||
object : Runnable {
|
||||
override fun run() {
|
||||
if (!isVisible) {
|
||||
@ -326,7 +326,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
checkDownloadProgress()
|
||||
|
||||
// Keep checking download progress at intervals of 1 second.
|
||||
mHandler.postDelayed(this, DOWNLOAD_PROGRESS_CHECK_DELAY)
|
||||
handler.postDelayed(this, DOWNLOAD_PROGRESS_CHECK_DELAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -336,10 +336,10 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
*/
|
||||
private fun startDownloadProgressChecker() {
|
||||
Timber.d("Starting download progress checker")
|
||||
mDownloadProgressChecker.run()
|
||||
mIsProgressCheckerRunning = true
|
||||
mDownloadPercentageText.text = getString(R.string.percentage, DOWNLOAD_STARTED_PROGRESS_PERCENTAGE)
|
||||
mDownloadProgressBar.progress = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE.toInt()
|
||||
downloadProgressChecker.run()
|
||||
isProgressCheckerRunning = true
|
||||
downloadPercentageText.text = getString(R.string.percentage, DOWNLOAD_STARTED_PROGRESS_PERCENTAGE)
|
||||
downloadProgressBar.progress = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE.toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -347,8 +347,8 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
*/
|
||||
private fun stopDownloadProgressChecker() {
|
||||
Timber.d("Stopping download progress checker")
|
||||
mHandler.removeCallbacks(mDownloadProgressChecker)
|
||||
mIsProgressCheckerRunning = false
|
||||
handler.removeCallbacks(downloadProgressChecker)
|
||||
isProgressCheckerRunning = false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,9 +356,9 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
*/
|
||||
private fun checkDownloadProgress() {
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterById(mDownloadId)
|
||||
query.setFilterById(downloadId)
|
||||
|
||||
val cursor = mDownloadManager.query(query)
|
||||
val cursor = downloadManager.query(query)
|
||||
|
||||
cursor.use {
|
||||
// Return if cursor is empty.
|
||||
@ -379,8 +379,8 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
// Show download progress percentage up to 1 decimal place.
|
||||
"%.1f".format(downloadProgress)
|
||||
}
|
||||
mDownloadPercentageText.text = getString(R.string.percentage, percentageValue)
|
||||
mDownloadProgressBar.progress = downloadProgress.toInt()
|
||||
downloadPercentageText.text = getString(R.string.percentage, percentageValue)
|
||||
downloadProgressBar.progress = downloadProgress.toInt()
|
||||
|
||||
val columnIndexForStatus = it.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||
val columnIndexForReason = it.getColumnIndex(DownloadManager.COLUMN_REASON)
|
||||
@ -399,9 +399,9 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
if (it.getInt(columnIndexForStatus) == DownloadManager.STATUS_PAUSED &&
|
||||
it.getInt(columnIndexForReason) == DownloadManager.PAUSED_WAITING_FOR_NETWORK
|
||||
) {
|
||||
mCheckNetworkInfoText.visibility = View.VISIBLE
|
||||
checkNetworkInfoText.visibility = View.VISIBLE
|
||||
} else {
|
||||
mCheckNetworkInfoText.visibility = View.GONE
|
||||
checkNetworkInfoText.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,7 +410,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
* Open the downloaded deck using 'mFileName'.
|
||||
*/
|
||||
private fun openDownloadedDeck(context: Context?) {
|
||||
val mimeType = URLConnection.guessContentTypeFromName(mFileName)
|
||||
val mimeType = URLConnection.guessContentTypeFromName(fileName)
|
||||
val fileIntent = Intent(context, IntentHandler::class.java)
|
||||
fileIntent.action = Intent.ACTION_VIEW
|
||||
|
||||
@ -419,7 +419,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
FileProvider.getUriForFile(
|
||||
it,
|
||||
it.applicationContext?.packageName + ".apkgfileprovider",
|
||||
File(sharedDecksPath, mFileName.toString())
|
||||
File(sharedDecksPath, fileName.toString())
|
||||
)
|
||||
}
|
||||
Timber.d("File URI -> $fileUri")
|
||||
@ -452,10 +452,10 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
Timber.i("Download failed, update UI and provide option to retry")
|
||||
context?.let { UIUtils.showThemedToast(it, R.string.something_wrong, false) }
|
||||
// Update UI if download could not be successful
|
||||
mTryAgainButton.visibility = View.VISIBLE
|
||||
mCancelButton.visibility = View.GONE
|
||||
mDownloadPercentageText.text = getString(R.string.download_failed)
|
||||
mDownloadProgressBar.progress = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE.toInt()
|
||||
tryAgainButton.visibility = View.VISIBLE
|
||||
cancelButton.visibility = View.GONE
|
||||
downloadPercentageText.text = getString(R.string.download_failed)
|
||||
downloadProgressBar.progress = DOWNLOAD_STARTED_PROGRESS_PERCENTAGE.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,7 +471,7 @@ class SharedDecksDownloadFragment : Fragment() {
|
||||
downloadCancelConfirmationDialog = AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.cancel_download_question_title)
|
||||
setPositiveButton(R.string.dialog_yes) { _, _ ->
|
||||
mDownloadManager.remove(mDownloadId)
|
||||
downloadManager.remove(downloadId)
|
||||
unregisterReceiver()
|
||||
isDownloadInProgress = false
|
||||
activity?.onBackPressed()
|
||||
|
@ -406,7 +406,7 @@ class MigrateStorageOnSyncSuccess(res: Resources) : AsyncOperation() {
|
||||
* @param messageResource String resource for message
|
||||
*/
|
||||
fun DeckPicker.showSyncLogMessage(@StringRes messageResource: Int, syncMessage: String?) {
|
||||
if (mActivityPaused) {
|
||||
if (activityPaused) {
|
||||
val res = AnkiDroidApp.appResources
|
||||
showSimpleNotification(
|
||||
res.getString(R.string.app_name),
|
||||
|
@ -66,7 +66,7 @@ class ExportReadyDialog(private val listener: ExportReadyDialogListener) : Async
|
||||
) {
|
||||
override fun handleAsyncMessage(deckPicker: DeckPicker) {
|
||||
deckPicker.showDialogFragment(
|
||||
deckPicker.mExportingDelegate.mDialogsFactory.newExportReadyDialog().withArguments(exportPath)
|
||||
deckPicker.exportingDelegate.dialogsFactory.newExportReadyDialog().withArguments(exportPath)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ class RecursivePictureMenu : DialogFragment() {
|
||||
val item = items[position]
|
||||
textView.setText(item.title)
|
||||
textView.setOnClickListener { item.execute(requireActivity() as AnkiActivity) }
|
||||
val icon = item.mIcon
|
||||
val icon = item.icon
|
||||
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, 0, 0, 0)
|
||||
}
|
||||
|
||||
@ -73,13 +73,13 @@ class RecursivePictureMenu : DialogFragment() {
|
||||
val title: Int
|
||||
|
||||
@DrawableRes
|
||||
val mIcon: Int
|
||||
private val mAnalyticsId: String?
|
||||
val icon: Int
|
||||
private val analyticsId: String?
|
||||
|
||||
constructor(@StringRes titleString: Int, @DrawableRes iconDrawable: Int, analyticsId: String?) {
|
||||
title = titleString
|
||||
mIcon = iconDrawable
|
||||
mAnalyticsId = analyticsId
|
||||
icon = iconDrawable
|
||||
this.analyticsId = analyticsId
|
||||
}
|
||||
|
||||
open val children: List<Item?>
|
||||
@ -87,8 +87,8 @@ class RecursivePictureMenu : DialogFragment() {
|
||||
|
||||
protected constructor(parcel: Parcel) {
|
||||
title = parcel.readInt()
|
||||
mIcon = parcel.readInt()
|
||||
mAnalyticsId = parcel.readString()
|
||||
icon = parcel.readInt()
|
||||
analyticsId = parcel.readString()
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
@ -97,13 +97,13 @@ class RecursivePictureMenu : DialogFragment() {
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(title)
|
||||
dest.writeInt(mIcon)
|
||||
dest.writeString(mAnalyticsId)
|
||||
dest.writeInt(icon)
|
||||
dest.writeString(analyticsId)
|
||||
}
|
||||
|
||||
protected abstract fun onClicked(activity: AnkiActivity)
|
||||
fun sendAnalytics() {
|
||||
UsageAnalytics.sendAnalyticsEvent(UsageAnalytics.Category.LINK_CLICKED, mAnalyticsId!!)
|
||||
UsageAnalytics.sendAnalyticsEvent(UsageAnalytics.Category.LINK_CLICKED, analyticsId!!)
|
||||
}
|
||||
|
||||
/* This method calls onClicked method to handle click event in a suitable manner and
|
||||
@ -118,14 +118,14 @@ class RecursivePictureMenu : DialogFragment() {
|
||||
}
|
||||
|
||||
class ItemHeader : Item, Parcelable {
|
||||
private val mChildren: MutableList<Item?>?
|
||||
private val _children: MutableList<Item?>?
|
||||
|
||||
constructor(@StringRes titleString: Int, i: Int, analyticsStringId: String?, vararg children: Item?) : super(titleString, i, analyticsStringId) {
|
||||
mChildren = ArrayList(listOf(*children))
|
||||
_children = ArrayList(listOf(*children))
|
||||
}
|
||||
|
||||
override val children: List<Item?>
|
||||
get() = ArrayList(mChildren!!.toMutableList())
|
||||
get() = ArrayList(_children!!.toMutableList())
|
||||
|
||||
public override fun onClicked(activity: AnkiActivity) {
|
||||
val children = ArrayList(children)
|
||||
@ -134,28 +134,28 @@ class RecursivePictureMenu : DialogFragment() {
|
||||
}
|
||||
|
||||
override fun remove(toRemove: Item?) {
|
||||
mChildren!!.remove(toRemove)
|
||||
for (i in mChildren) {
|
||||
_children!!.remove(toRemove)
|
||||
for (i in _children) {
|
||||
i!!.remove(toRemove)
|
||||
}
|
||||
}
|
||||
|
||||
protected constructor(parcel: Parcel) : super(parcel) {
|
||||
if (parcel.readByte().toInt() == 0x01) {
|
||||
mChildren = ArrayList()
|
||||
ParcelCompat.readList(parcel, mChildren, Item::class.java.classLoader, Item::class.java)
|
||||
_children = ArrayList()
|
||||
ParcelCompat.readList(parcel, _children, Item::class.java.classLoader, Item::class.java)
|
||||
} else {
|
||||
mChildren = ArrayList(0)
|
||||
_children = ArrayList(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
super.writeToParcel(dest, flags)
|
||||
if (mChildren == null) {
|
||||
if (_children == null) {
|
||||
dest.writeByte(0x00.toByte())
|
||||
} else {
|
||||
dest.writeByte(0x01.toByte())
|
||||
dest.writeList(mChildren)
|
||||
dest.writeList(_children)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,13 +46,13 @@ import java.util.function.Supplier
|
||||
* A delegate class used in any [AnkiActivity] where the exporting feature is required.
|
||||
*
|
||||
* Must be constructed before calling [AnkiActivity.onCreate(Bundle, PersistableBundle)][AnkiActivity.onCreate],
|
||||
* to ensure the fragment factory ([mDialogsFactory]) is set correctly.
|
||||
* to ensure the fragment factory ([dialogsFactory]) is set correctly.
|
||||
*
|
||||
* @param activity the calling activity (must implement [ExportReadyDialogListener])
|
||||
* @param collectionSupplier a predicate that supplies a collection instance
|
||||
*/
|
||||
class ActivityExportingDelegate(private val activity: AnkiActivity, private val collectionSupplier: Supplier<Collection>) : ExportReadyDialogListener {
|
||||
val mDialogsFactory: ExportDialogsFactory
|
||||
val dialogsFactory: ExportDialogsFactory
|
||||
private val saveFileLauncher: ActivityResultLauncher<Intent>
|
||||
private lateinit var fileExportPath: String
|
||||
|
||||
@ -186,8 +186,8 @@ class ActivityExportingDelegate(private val activity: AnkiActivity, private val
|
||||
|
||||
init {
|
||||
val fragmentManager = activity.supportFragmentManager
|
||||
mDialogsFactory = ExportDialogsFactory(this).attachToActivity(activity)
|
||||
fragmentManager.fragmentFactory = mDialogsFactory
|
||||
dialogsFactory = ExportDialogsFactory(this).attachToActivity(activity)
|
||||
fragmentManager.fragmentFactory = dialogsFactory
|
||||
saveFileLauncher = activity.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result: ActivityResult ->
|
||||
|
@ -28,8 +28,8 @@ import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
class AudioRecorder {
|
||||
private lateinit var mRecorder: MediaRecorder
|
||||
private var mOnRecordingInitialized: Runnable? = null
|
||||
private lateinit var recorder: MediaRecorder
|
||||
private var onRecordingInitialized: Runnable? = null
|
||||
private var previousNonZeroAmplitude = 0
|
||||
private fun initMediaRecorder(context: Context, audioPath: String): MediaRecorder {
|
||||
val mr = CompatHelper.compat.getMediaRecorder(context)
|
||||
@ -41,7 +41,7 @@ class AudioRecorder {
|
||||
}
|
||||
|
||||
private fun onRecordingInitialized() {
|
||||
mOnRecordingInitialized?.run()
|
||||
onRecordingInitialized?.run()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@ -50,14 +50,14 @@ class AudioRecorder {
|
||||
try {
|
||||
// try high quality AAC @ 44.1kHz / 192kbps first
|
||||
// can throw IllegalArgumentException if codec isn't supported
|
||||
mRecorder = initMediaRecorder(context, audioPath)
|
||||
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
||||
mRecorder.setAudioChannels(2)
|
||||
mRecorder.setAudioSamplingRate(44100)
|
||||
mRecorder.setAudioEncodingBitRate(192000)
|
||||
recorder = initMediaRecorder(context, audioPath)
|
||||
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
||||
recorder.setAudioChannels(2)
|
||||
recorder.setAudioSamplingRate(44100)
|
||||
recorder.setAudioEncodingBitRate(192000)
|
||||
// this can also throw IOException if output path is invalid
|
||||
mRecorder.prepare()
|
||||
mRecorder.start()
|
||||
recorder.prepare()
|
||||
recorder.start()
|
||||
highSampling = true
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e)
|
||||
@ -66,32 +66,32 @@ class AudioRecorder {
|
||||
if (!highSampling) {
|
||||
// if we are here, either the codec didn't work or output file was invalid
|
||||
// fall back on default
|
||||
mRecorder = initMediaRecorder(context, audioPath)
|
||||
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
|
||||
mRecorder.prepare()
|
||||
mRecorder.start()
|
||||
recorder = initMediaRecorder(context, audioPath)
|
||||
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
|
||||
recorder.prepare()
|
||||
recorder.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopRecording() {
|
||||
if (this::mRecorder.isInitialized) {
|
||||
mRecorder.stop()
|
||||
if (this::recorder.isInitialized) {
|
||||
recorder.stop()
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnRecordingInitializedHandler(onRecordingInitialized: Runnable?) {
|
||||
mOnRecordingInitialized = onRecordingInitialized
|
||||
this.onRecordingInitialized = onRecordingInitialized
|
||||
}
|
||||
|
||||
fun release() {
|
||||
if (this::mRecorder.isInitialized) {
|
||||
mRecorder.release()
|
||||
if (this::recorder.isInitialized) {
|
||||
recorder.release()
|
||||
}
|
||||
}
|
||||
|
||||
fun maxAmplitude(): Int {
|
||||
val currentAmplitude = if (this::mRecorder.isInitialized) {
|
||||
mRecorder.maxAmplitude
|
||||
val currentAmplitude = if (this::recorder.isInitialized) {
|
||||
recorder.maxAmplitude
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@ -104,22 +104,22 @@ class AudioRecorder {
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
if (!this::mRecorder.isInitialized) {
|
||||
if (!this::recorder.isInitialized) {
|
||||
return
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
mRecorder.pause()
|
||||
recorder.pause()
|
||||
} else {
|
||||
mRecorder.stop()
|
||||
recorder.stop()
|
||||
}
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
if (this::mRecorder.isInitialized) {
|
||||
if (this::recorder.isInitialized) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
mRecorder.resume()
|
||||
recorder.resume()
|
||||
} else {
|
||||
mRecorder.start()
|
||||
recorder.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ import java.text.DecimalFormat
|
||||
|
||||
@KotlinCleanup("lateinit")
|
||||
class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCallback {
|
||||
private lateinit var mField: IField
|
||||
private lateinit var mNote: IMultimediaEditableNote
|
||||
private var mFieldIndex = 0
|
||||
private lateinit var field: IField
|
||||
private lateinit var note: IMultimediaEditableNote
|
||||
private var fieldIndex = 0
|
||||
|
||||
@get:VisibleForTesting
|
||||
var fieldController: IFieldController? = null
|
||||
@ -57,7 +57,7 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
* Cached copy of the current request to change a field
|
||||
* Used to access past state from OnRequestPermissionsResultCallback
|
||||
*/
|
||||
private var mCurrentChangeRequest: ChangeUIRequest? = null
|
||||
private var currentChangeRequest: ChangeUIRequest? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (showedActivityFailedScreen(savedInstanceState)) {
|
||||
return
|
||||
@ -85,11 +85,11 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
finishCancel()
|
||||
return
|
||||
}
|
||||
mFieldIndex = extras.first
|
||||
mField = extras.second
|
||||
mNote = extras.third
|
||||
fieldIndex = extras.first
|
||||
field = extras.second
|
||||
note = extras.third
|
||||
onBack()
|
||||
recreateEditingUi(ChangeUIRequest.init(mField), controllerBundle)
|
||||
recreateEditingUi(ChangeUIRequest.init(field), controllerBundle)
|
||||
}
|
||||
|
||||
// in case media is saved by view button then allows it to be inserted into the filed
|
||||
@ -122,9 +122,9 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
|
||||
private fun setupUIController(fieldController: IFieldController, savedInstanceState: Bundle?) {
|
||||
fieldController.apply {
|
||||
setField(mField)
|
||||
setFieldIndex(mFieldIndex)
|
||||
setNote(mNote)
|
||||
setField(field)
|
||||
setFieldIndex(fieldIndex)
|
||||
setNote(note)
|
||||
setEditingActivity(this@MultimediaEditFieldActivity)
|
||||
loadInstanceState(savedInstanceState)
|
||||
}
|
||||
@ -134,7 +134,7 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
Timber.d("recreateEditingUi()")
|
||||
|
||||
// Permissions are checked async, save our current state to allow continuation
|
||||
mCurrentChangeRequest = newUI
|
||||
currentChangeRequest = newUI
|
||||
|
||||
// If we went through the permission check once, we don't need to do it again.
|
||||
// As we only get here a second time if we have the required permissions
|
||||
@ -145,7 +145,7 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
val fieldController = createControllerForField(newUI.field)
|
||||
UIRecreationHandler.onPreFieldControllerReplacement(this.fieldController)
|
||||
this.fieldController = fieldController
|
||||
mField = newUI.field
|
||||
field = newUI.field
|
||||
setupUIController(this.fieldController!!, savedInstanceState)
|
||||
val linearLayout = findViewById<LinearLayout>(R.id.LinearLayoutInScrollViewFieldEdit)
|
||||
linearLayout.removeAllViews()
|
||||
@ -154,13 +154,13 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
Timber.d("onCreateOptionsMenu() - mField.getType() = %s", mField.type)
|
||||
Timber.d("onCreateOptionsMenu() - mField.getType() = %s", field.type)
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.activity_edit_text, menu)
|
||||
menu.findItem(R.id.multimedia_edit_field_to_text).isVisible = mField.type !== EFieldType.TEXT
|
||||
menu.findItem(R.id.multimedia_edit_field_to_audio).isVisible = mField.type !== EFieldType.AUDIO_RECORDING
|
||||
menu.findItem(R.id.multimedia_edit_field_to_audio_clip).isVisible = mField.type !== EFieldType.MEDIA_CLIP
|
||||
menu.findItem(R.id.multimedia_edit_field_to_image).isVisible = mField.type !== EFieldType.IMAGE
|
||||
menu.findItem(R.id.multimedia_edit_field_to_text).isVisible = field.type !== EFieldType.TEXT
|
||||
menu.findItem(R.id.multimedia_edit_field_to_audio).isVisible = field.type !== EFieldType.AUDIO_RECORDING
|
||||
menu.findItem(R.id.multimedia_edit_field_to_audio_clip).isVisible = field.type !== EFieldType.MEDIA_CLIP
|
||||
menu.findItem(R.id.multimedia_edit_field_to_image).isVisible = field.type !== EFieldType.IMAGE
|
||||
return true
|
||||
}
|
||||
|
||||
@ -198,12 +198,12 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
@KotlinCleanup("rename: bChangeToText")
|
||||
private fun done() {
|
||||
var bChangeToText = false
|
||||
if (mField.type === EFieldType.IMAGE) {
|
||||
if (mField.imagePath == null) {
|
||||
if (field.type === EFieldType.IMAGE) {
|
||||
if (field.imagePath == null) {
|
||||
bChangeToText = true
|
||||
}
|
||||
if (!bChangeToText) {
|
||||
val f = File(mField.imagePath!!)
|
||||
val f = File(field.imagePath!!)
|
||||
if (!f.exists()) {
|
||||
bChangeToText = true
|
||||
} else {
|
||||
@ -214,12 +214,12 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mField.type === EFieldType.AUDIO_RECORDING) {
|
||||
if (mField.audioPath == null) {
|
||||
} else if (field.type === EFieldType.AUDIO_RECORDING) {
|
||||
if (field.audioPath == null) {
|
||||
bChangeToText = true
|
||||
}
|
||||
if (!bChangeToText) {
|
||||
val f = File(mField.audioPath!!)
|
||||
val f = File(field.audioPath!!)
|
||||
if (!f.exists()) {
|
||||
bChangeToText = true
|
||||
}
|
||||
@ -230,28 +230,28 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
}
|
||||
|
||||
private fun toAudioRecordingField() {
|
||||
if (mField.type !== EFieldType.AUDIO_RECORDING) {
|
||||
if (field.type !== EFieldType.AUDIO_RECORDING) {
|
||||
val request = ChangeUIRequest.uiChange(AudioRecordingField())
|
||||
recreateEditingUi(request)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toAudioClipField() {
|
||||
if (mField.type !== EFieldType.MEDIA_CLIP) {
|
||||
if (field.type !== EFieldType.MEDIA_CLIP) {
|
||||
val request = ChangeUIRequest.uiChange(MediaClipField())
|
||||
recreateEditingUi(request)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toImageField() {
|
||||
if (mField.type !== EFieldType.IMAGE) {
|
||||
if (field.type !== EFieldType.IMAGE) {
|
||||
val request = ChangeUIRequest.uiChange(ImageField())
|
||||
recreateEditingUi(request)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toTextField() {
|
||||
if (mField.type !== EFieldType.TEXT) {
|
||||
if (field.type !== EFieldType.TEXT) {
|
||||
val request = ChangeUIRequest.uiChange(TextField())
|
||||
recreateEditingUi(request)
|
||||
}
|
||||
@ -259,15 +259,15 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
|
||||
private fun recreateEditingUIUsingCachedRequest() {
|
||||
Timber.d("recreateEditingUIUsingCachedRequest()")
|
||||
if (mCurrentChangeRequest == null) {
|
||||
if (currentChangeRequest == null) {
|
||||
cancelActivityWithAssertionFailure("mCurrentChangeRequest should be set before using cached request")
|
||||
return
|
||||
}
|
||||
recreateEditingUi(mCurrentChangeRequest!!)
|
||||
recreateEditingUi(currentChangeRequest!!)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
if (mCurrentChangeRequest == null) {
|
||||
if (currentChangeRequest == null) {
|
||||
cancelActivityWithAssertionFailure("mCurrentChangeRequest should be set before requesting permissions")
|
||||
return
|
||||
}
|
||||
@ -283,7 +283,7 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
resources.getString(R.string.multimedia_editor_audio_permission_refused),
|
||||
true
|
||||
)
|
||||
UIRecreationHandler.onRequiredPermissionDenied(mCurrentChangeRequest!!, this)
|
||||
UIRecreationHandler.onRequiredPermissionDenied(currentChangeRequest!!, this)
|
||||
}
|
||||
if (requestCode == REQUEST_CAMERA_PERMISSION && permissions.size == 1) {
|
||||
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
@ -319,8 +319,8 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
|
||||
|
||||
private fun saveAndExit(ignoreField: Boolean = false) {
|
||||
val resultData = Intent().apply {
|
||||
putExtra(EXTRA_RESULT_FIELD, if (ignoreField) null else mField)
|
||||
putExtra(EXTRA_RESULT_FIELD_INDEX, mFieldIndex)
|
||||
putExtra(EXTRA_RESULT_FIELD, if (ignoreField) null else field)
|
||||
putExtra(EXTRA_RESULT_FIELD_INDEX, fieldIndex)
|
||||
}
|
||||
setResult(RESULT_OK, resultData)
|
||||
finish()
|
||||
|
@ -20,21 +20,23 @@
|
||||
package com.ichi2.anki.multimediacard.fields
|
||||
|
||||
import com.ichi2.libanki.Collection
|
||||
import com.ichi2.utils.KotlinCleanup
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Implementation of Audio field types
|
||||
*/
|
||||
@KotlinCleanup("replace _audioPath with `field`")
|
||||
abstract class AudioField : FieldBase(), IField {
|
||||
private var mAudioPath: String? = null
|
||||
private var _audioPath: String? = null
|
||||
|
||||
override var imagePath: String? = null
|
||||
|
||||
override var audioPath: String?
|
||||
get() = mAudioPath
|
||||
get() = _audioPath
|
||||
set(value) {
|
||||
mAudioPath = value
|
||||
_audioPath = value
|
||||
setThisModified()
|
||||
}
|
||||
|
||||
|
@ -74,16 +74,16 @@ import kotlin.math.roundToLong
|
||||
|
||||
class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
@KotlinCleanup("lateinit wherever possible")
|
||||
private lateinit var mImagePreview: ImageView
|
||||
private lateinit var mImageFileSize: TextView
|
||||
private lateinit var mImageFileSizeWarning: TextView
|
||||
private var mViewModel = ImageViewModel(null, null)
|
||||
private var mPreviousImagePath: String? = null // save the latest path to prevent from cropping or taking photo action canceled
|
||||
private var mPreviousImageUri: Uri? = null
|
||||
private var mAnkiCacheDirectory: String? = null // system provided 'External Cache Dir' with "temp-photos" on it
|
||||
private lateinit var imagePreview: ImageView
|
||||
private lateinit var imageFileSize: TextView
|
||||
private lateinit var imageFileSizeWarning: TextView
|
||||
private var viewModel = ImageViewModel(null, null)
|
||||
private var previousImagePath: String? = null // save the latest path to prevent from cropping or taking photo action canceled
|
||||
private var previousImageUri: Uri? = null
|
||||
private var ankiCacheDirectory: String? = null // system provided 'External Cache Dir' with "temp-photos" on it
|
||||
// e.g. '/self/primary/Android/data/com.ichi2.anki.AnkiDroid/cache/temp-photos'
|
||||
|
||||
private lateinit var mCropButton: Button
|
||||
private lateinit var cropButton: Button
|
||||
private val maxImageSize: Int
|
||||
get() {
|
||||
val metrics = displayMetrics
|
||||
@ -117,14 +117,14 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
// Some apps send this back with app-specific data, direct the user to another app
|
||||
if (result.resultCode >= Activity.RESULT_FIRST_USER) {
|
||||
UIUtils.showThemedToast(mActivity, mActivity.getString(R.string.activity_result_unexpected), true)
|
||||
UIUtils.showThemedToast(_activity, _activity.getString(R.string.activity_result_unexpected), true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mImageFileSizeWarning.visibility = View.GONE
|
||||
imageFileSizeWarning.visibility = View.GONE
|
||||
onSuccess(result)
|
||||
setPreviewImage(mViewModel.imagePath, maxImageSize)
|
||||
setPreviewImage(viewModel.imagePath, maxImageSize)
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,25 +135,25 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
Timber.i("loadInstanceState loading saved state...")
|
||||
mViewModel = ImageViewModel.fromBundle(savedInstanceState)
|
||||
mPreviousImagePath = savedInstanceState.getString("mPreviousImagePath")
|
||||
mPreviousImageUri = BundleCompat.getParcelable(savedInstanceState, "mPreviousImageUri", Uri::class.java)
|
||||
viewModel = ImageViewModel.fromBundle(savedInstanceState)
|
||||
previousImagePath = savedInstanceState.getString("mPreviousImagePath")
|
||||
previousImageUri = BundleCompat.getParcelable(savedInstanceState, "mPreviousImageUri", Uri::class.java)
|
||||
}
|
||||
|
||||
override fun saveInstanceState(): Bundle {
|
||||
Timber.d("saveInstanceState")
|
||||
val savedInstanceState = Bundle()
|
||||
mViewModel.enrich(savedInstanceState)
|
||||
savedInstanceState.putString("mPreviousImagePath", mPreviousImagePath)
|
||||
savedInstanceState.putParcelable("mPreviousImageUri", mPreviousImageUri)
|
||||
viewModel.enrich(savedInstanceState)
|
||||
savedInstanceState.putString("mPreviousImagePath", previousImagePath)
|
||||
savedInstanceState.putParcelable("mPreviousImageUri", previousImageUri)
|
||||
return savedInstanceState
|
||||
}
|
||||
|
||||
override fun createUI(context: Context, layout: LinearLayout) {
|
||||
Timber.d("createUI()")
|
||||
mViewModel = mViewModel.replaceNullValues(mField, mActivity)
|
||||
viewModel = viewModel.replaceNullValues(_field, _activity)
|
||||
|
||||
mImagePreview = ImageView(mActivity)
|
||||
imagePreview = ImageView(_activity)
|
||||
val externalCacheDirRoot = context.externalCacheDir
|
||||
if (externalCacheDirRoot == null) {
|
||||
Timber.e("createUI() unable to get external cache directory")
|
||||
@ -166,7 +166,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
showSomethingWentWrong()
|
||||
return
|
||||
}
|
||||
mAnkiCacheDirectory = externalCacheDir.absolutePath
|
||||
ankiCacheDirectory = externalCacheDir.absolutePath
|
||||
|
||||
val p = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
@ -175,13 +175,13 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
drawUIComponents(context)
|
||||
|
||||
mCropButton = Button(mActivity).apply {
|
||||
cropButton = Button(_activity).apply {
|
||||
text = gtxt(R.string.crop_button)
|
||||
setOnClickListener { mViewModel = requestCrop(mViewModel) }
|
||||
setOnClickListener { viewModel = requestCrop(viewModel) }
|
||||
visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
val btnGallery = Button(mActivity).apply {
|
||||
val btnGallery = Button(_activity).apply {
|
||||
text = gtxt(R.string.multimedia_editor_image_field_editing_galery)
|
||||
setOnClickListener {
|
||||
val i = Intent(Intent.ACTION_PICK)
|
||||
@ -190,26 +190,26 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
}
|
||||
|
||||
val btnDraw = Button(mActivity).apply {
|
||||
val btnDraw = Button(_activity).apply {
|
||||
text = gtxt(R.string.drawing)
|
||||
setOnClickListener {
|
||||
drawingLauncher.launch(Intent(mActivity, DrawingActivity::class.java))
|
||||
drawingLauncher.launch(Intent(_activity, DrawingActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
val btnCamera = Button(mActivity).apply {
|
||||
val btnCamera = Button(_activity).apply {
|
||||
text = gtxt(R.string.multimedia_editor_image_field_editing_photo)
|
||||
setOnClickListener { mViewModel = captureImage(context) }
|
||||
setOnClickListener { viewModel = captureImage(context) }
|
||||
if (!canUseCamera(context)) {
|
||||
visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
setPreviewImage(mViewModel.imagePath, maxImageSize)
|
||||
setPreviewImage(viewModel.imagePath, maxImageSize)
|
||||
|
||||
layout.addView(mImagePreview, ViewGroup.LayoutParams.MATCH_PARENT, p)
|
||||
layout.addView(mImageFileSize, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
layout.addView(mImageFileSizeWarning, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
layout.addView(imagePreview, ViewGroup.LayoutParams.MATCH_PARENT, p)
|
||||
layout.addView(imageFileSize, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
layout.addView(imageFileSizeWarning, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
layout.addView(btnGallery, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
// drew image appear far larger in preview in devices API < 24 #9439
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
@ -218,22 +218,22 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
if (canUseCamera(context)) {
|
||||
layout.addView(btnCamera, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
layout.addView(mCropButton, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
layout.addView(cropButton, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
|
||||
override fun setEditingActivity(activity: MultimediaEditFieldActivity) {
|
||||
super.setEditingActivity(activity)
|
||||
val registryToUse = if (this::registryToUse.isInitialized) registryToUse else mActivity.activityResultRegistry
|
||||
val registryToUse = if (this::registryToUse.isInitialized) registryToUse else this._activity.activityResultRegistry
|
||||
@NeedsTest("check the happy/failure path for the crop action")
|
||||
cropImageRequest = registryToUse.register(CROP_IMAGE_LAUNCHER_KEY, CropImageContract()) { cropResult ->
|
||||
if (cropResult.isSuccessful) {
|
||||
mImageFileSizeWarning.visibility = View.GONE
|
||||
imageFileSizeWarning.visibility = View.GONE
|
||||
if (cropResult != null) {
|
||||
handleCropResult(cropResult)
|
||||
}
|
||||
setPreviewImage(mViewModel.imagePath, maxImageSize)
|
||||
setPreviewImage(viewModel.imagePath, maxImageSize)
|
||||
} else {
|
||||
if (!mPreviousImagePath.isNullOrEmpty()) {
|
||||
if (!previousImagePath.isNullOrEmpty()) {
|
||||
revertToPreviousImage()
|
||||
}
|
||||
// cropImage can give us more information. Not sure it is actionable so for now just log it.
|
||||
@ -268,7 +268,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
onSuccess = {
|
||||
try {
|
||||
handleSelectImageIntent(it.data)
|
||||
mImageFileSizeWarning.visibility = View.GONE
|
||||
imageFileSizeWarning.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
CrashReportService.sendExceptionReport(e, "BasicImageFieldController - handleSelectImageIntent")
|
||||
Timber.e(e, "Failed to select image")
|
||||
@ -315,7 +315,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
private fun captureImage(context: Context): ImageViewModel {
|
||||
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
val image: File
|
||||
var toReturn = mViewModel
|
||||
var toReturn = viewModel
|
||||
try {
|
||||
saveImageForRevert()
|
||||
|
||||
@ -339,28 +339,28 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
private fun saveImageForRevert() {
|
||||
if (!mViewModel.isPreExistingImage) {
|
||||
if (!viewModel.isPreExistingImage) {
|
||||
deletePreviousImage()
|
||||
}
|
||||
mPreviousImagePath = mViewModel.imagePath
|
||||
mPreviousImageUri = mViewModel.imageUri
|
||||
previousImagePath = viewModel.imagePath
|
||||
previousImageUri = viewModel.imageUri
|
||||
}
|
||||
|
||||
private fun deletePreviousImage() {
|
||||
// Store the old image path for deletion / error handling if the user cancels
|
||||
if (mPreviousImagePath != null && !File(mPreviousImagePath!!).delete()) {
|
||||
if (previousImagePath != null && !File(previousImagePath!!).delete()) {
|
||||
Timber.i("deletePreviousImage() unable to delete previous image file")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun createNewCacheImageFile(extension: String = "jpg"): File {
|
||||
val storageDir = File(mAnkiCacheDirectory!!)
|
||||
val storageDir = File(ankiCacheDirectory!!)
|
||||
return File.createTempFile("img", ".$extension", storageDir)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun createCachedFile(filename: String) = File(mAnkiCacheDirectory, filename).apply {
|
||||
private fun createCachedFile(filename: String) = File(ankiCacheDirectory, filename).apply {
|
||||
deleteOnExit()
|
||||
}
|
||||
|
||||
@ -370,7 +370,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
val height = metrics.heightPixels
|
||||
val width = metrics.widthPixels
|
||||
|
||||
mImagePreview = ImageView(mActivity).apply {
|
||||
imagePreview = ImageView(_activity).apply {
|
||||
scaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||
adjustViewBounds = true
|
||||
|
||||
@ -378,7 +378,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
maxWidth = (width * 0.6).roundToLong().toInt()
|
||||
}
|
||||
|
||||
mImageFileSize = FixedEditText(context).apply {
|
||||
imageFileSize = FixedEditText(context).apply {
|
||||
maxWidth = (width * 0.6).roundToLong().toInt()
|
||||
isEnabled = false
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
@ -388,7 +388,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
// #5513 - Image compression failed, but we'll confuse most users if we tell them that. Instead, just imply that
|
||||
// there's an action that they can take.
|
||||
mImageFileSizeWarning = FixedEditText(context).apply {
|
||||
imageFileSizeWarning = FixedEditText(context).apply {
|
||||
maxWidth = (width * 0.6).roundToLong().toInt()
|
||||
isEnabled = false
|
||||
setTextColor(Color.parseColor("#FF4500")) // Orange-Red
|
||||
@ -399,34 +399,34 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
private fun gtxt(id: Int): String {
|
||||
return mActivity.getText(id).toString()
|
||||
return _activity.getText(id).toString()
|
||||
}
|
||||
|
||||
// #9333: getDefaultDisplay & getMetrics
|
||||
@Suppress("deprecation") // defaultDisplay
|
||||
private val displayMetrics: DisplayMetrics by lazy {
|
||||
DisplayMetrics().apply {
|
||||
mActivity.windowManager.defaultDisplay.getMetrics(this)
|
||||
_activity.windowManager.defaultDisplay.getMetrics(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelImageCapture() {
|
||||
if (!mPreviousImagePath.isNullOrEmpty()) {
|
||||
if (!previousImagePath.isNullOrEmpty()) {
|
||||
revertToPreviousImage()
|
||||
}
|
||||
}
|
||||
|
||||
private fun revertToPreviousImage() {
|
||||
mViewModel.deleteImagePath()
|
||||
mViewModel = ImageViewModel(mPreviousImagePath, mPreviousImageUri)
|
||||
mField.imagePath = mPreviousImagePath
|
||||
mPreviousImagePath = null
|
||||
mPreviousImageUri = null
|
||||
viewModel.deleteImagePath()
|
||||
viewModel = ImageViewModel(previousImagePath, previousImageUri)
|
||||
_field.imagePath = previousImagePath
|
||||
previousImagePath = null
|
||||
previousImageUri = null
|
||||
}
|
||||
|
||||
private fun showSomethingWentWrong() {
|
||||
try {
|
||||
UIUtils.showThemedToast(mActivity, mActivity.resources.getString(R.string.multimedia_editor_something_wrong), false)
|
||||
UIUtils.showThemedToast(_activity, _activity.resources.getString(R.string.multimedia_editor_something_wrong), false)
|
||||
} catch (e: Exception) {
|
||||
// ignore. A NullPointerException may occur in Robolectric
|
||||
Timber.w(e, "Failed to display toast")
|
||||
@ -434,7 +434,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
private fun showSVGPreviewToast() {
|
||||
UIUtils.showThemedToast(mActivity, mActivity.resources.getString(R.string.multimedia_editor_svg_preview), false)
|
||||
UIUtils.showThemedToast(_activity, _activity.resources.getString(R.string.multimedia_editor_svg_preview), false)
|
||||
}
|
||||
|
||||
private fun handleSelectImageIntent(data: Intent?) {
|
||||
@ -450,7 +450,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
if (data.extras == null) "null" else data.extras!!.keySet().joinToString(", ")
|
||||
)
|
||||
|
||||
val selectedImage = getImageUri(mActivity, data)
|
||||
val selectedImage = getImageUri(_activity, data)
|
||||
if (selectedImage == null) {
|
||||
Timber.w("handleSelectImageIntent() selectedImage was null")
|
||||
showSomethingWentWrong()
|
||||
@ -465,7 +465,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
val imagePath = internalizedPick.absolutePath
|
||||
mViewModel = ImageViewModel(imagePath, getUriForFile(internalizedPick))
|
||||
viewModel = ImageViewModel(imagePath, getUriForFile(internalizedPick))
|
||||
setTemporaryMedia(imagePath)
|
||||
|
||||
Timber.i("handleSelectImageIntent() Decoded image: '%s'", imagePath)
|
||||
@ -486,7 +486,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
val drewImagePath = internalizedPick.absolutePath
|
||||
mViewModel = ImageViewModel(drewImagePath, imageUri)
|
||||
viewModel = ImageViewModel(drewImagePath, imageUri)
|
||||
setTemporaryMedia(drewImagePath)
|
||||
|
||||
Timber.i("handleDrawingResult() Decoded image: '%s'", drewImagePath)
|
||||
@ -494,7 +494,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
private fun internalizeUri(uri: Uri): File? {
|
||||
val internalFile: File
|
||||
val uriFileName = getImageNameFromUri(mActivity, uri)
|
||||
val uriFileName = getImageNameFromUri(_activity, uri)
|
||||
|
||||
// Use the display name from the image info to create a new file with correct extension
|
||||
if (uriFileName == null) {
|
||||
@ -510,7 +510,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
return null
|
||||
}
|
||||
return try {
|
||||
val returnFile = FileUtil.internalizeUri(uri, internalFile, mActivity.contentResolver)
|
||||
val returnFile = FileUtil.internalizeUri(uri, internalFile, _activity.contentResolver)
|
||||
Timber.d("internalizeUri successful. Returning internalFile.")
|
||||
returnFile
|
||||
} catch (e: Exception) {
|
||||
@ -569,8 +569,8 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
if (!f.delete()) {
|
||||
Timber.w("rotateAndCompress() delete of pre-compressed image failed %s", imagePath)
|
||||
}
|
||||
mViewModel = imageViewModel.rotateAndCompressTo(outFile.absolutePath, getUriForFile(outFile))
|
||||
mField.imagePath = outFile.absolutePath
|
||||
viewModel = imageViewModel.rotateAndCompressTo(outFile.absolutePath, getUriForFile(outFile))
|
||||
_field.imagePath = outFile.absolutePath
|
||||
Timber.d("rotateAndCompress out path %s has size %d", outFile.absolutePath, outFile.length())
|
||||
} catch (e: FileNotFoundException) {
|
||||
Timber.w(e, "rotateAndCompress() File not found for image compression %s", imagePath)
|
||||
@ -607,22 +607,22 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
Timber.d("setPreviewImage path %s has size %d", f.absolutePath, f.length())
|
||||
b = ExifUtil.rotateFromCamera(f, b)
|
||||
onValidImage(b, Formatter.formatFileSize(mActivity, f.length()))
|
||||
onValidImage(b, Formatter.formatFileSize(_activity, f.length()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun onValidImage(b: Bitmap, fileSize: String) {
|
||||
mImagePreview.setImageBitmap(b)
|
||||
mImageFileSize.visibility = View.VISIBLE
|
||||
mImageFileSize.text = fileSize
|
||||
mCropButton.visibility = View.VISIBLE
|
||||
imagePreview.setImageBitmap(b)
|
||||
imageFileSize.visibility = View.VISIBLE
|
||||
imageFileSize.text = fileSize
|
||||
cropButton.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
// ensure the previous preview is not visible
|
||||
private fun hideImagePreview() {
|
||||
BitmapUtil.freeImageView(mImagePreview)
|
||||
if (::mCropButton.isInitialized) mCropButton.visibility = View.INVISIBLE
|
||||
if (::mImageFileSize.isInitialized) mImageFileSize.visibility = View.INVISIBLE
|
||||
BitmapUtil.freeImageView(imagePreview)
|
||||
if (::cropButton.isInitialized) cropButton.visibility = View.INVISIBLE
|
||||
if (::imageFileSize.isInitialized) imageFileSize.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@ -635,7 +635,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
Timber.i("handleTakePictureResult appears to have an invalid picture")
|
||||
return
|
||||
}
|
||||
showCropDialog(mActivity.getString(R.string.crop_image), null)
|
||||
showCropDialog(_activity.getString(R.string.crop_image), null)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -677,22 +677,22 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
private fun setTemporaryMedia(imagePath: String) {
|
||||
mField.apply {
|
||||
_field.apply {
|
||||
this.imagePath = imagePath
|
||||
hasTemporaryMedia = true
|
||||
}
|
||||
}
|
||||
|
||||
fun showCropDialog(content: String, negativeCallback: (() -> Unit)?) {
|
||||
if (!mViewModel.isValid) {
|
||||
if (!viewModel.isValid) {
|
||||
Timber.w("showCropDialog called with null URI or Path")
|
||||
return
|
||||
}
|
||||
|
||||
AlertDialog.Builder(mActivity).show {
|
||||
AlertDialog.Builder(_activity).show {
|
||||
message(text = content)
|
||||
positiveButton(R.string.dialog_yes) {
|
||||
mViewModel = requestCrop(mViewModel)
|
||||
viewModel = requestCrop(viewModel)
|
||||
}
|
||||
negativeButton(R.string.dialog_no) {
|
||||
negativeCallback?.invoke() // Using invoke since negativeCallback is nullable
|
||||
@ -702,28 +702,28 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
private fun handleCropResult(result: CropImageView.CropResult) {
|
||||
Timber.d("handleCropResult")
|
||||
mViewModel.deleteImagePath()
|
||||
mViewModel = ImageViewModel(result.getUriFilePath(mActivity, true), result.uriContent)
|
||||
viewModel.deleteImagePath()
|
||||
viewModel = ImageViewModel(result.getUriFilePath(_activity, true), result.uriContent)
|
||||
if (!rotateAndCompress()) {
|
||||
Timber.i("handleCropResult() appears to have an invalid file, reverting")
|
||||
return
|
||||
}
|
||||
Timber.d("handleCropResult() = image path now %s", mField.imagePath)
|
||||
Timber.d("handleCropResult() = image path now %s", _field.imagePath)
|
||||
}
|
||||
|
||||
private fun rotateAndCompress(): Boolean {
|
||||
if (!rotateAndCompress(mViewModel.imagePath!!, mViewModel)) {
|
||||
mImageFileSizeWarning.visibility = View.VISIBLE
|
||||
if (!rotateAndCompress(viewModel.imagePath!!, viewModel)) {
|
||||
imageFileSizeWarning.visibility = View.VISIBLE
|
||||
revertToPreviousImage()
|
||||
showSomethingWentWrong()
|
||||
return false
|
||||
}
|
||||
mField.hasTemporaryMedia = true
|
||||
_field.hasTemporaryMedia = true
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getUriForFile(file: File): Uri {
|
||||
return getUriForFile(file, mActivity)
|
||||
return getUriForFile(file, _activity)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -800,7 +800,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
|
||||
val isShowingPreview: Boolean
|
||||
get() = mImageFileSize.visibility == View.VISIBLE
|
||||
get() = imageFileSize.visibility == View.VISIBLE
|
||||
|
||||
private class ImageViewModel(val imagePath: String?, val imageUri: Uri?) {
|
||||
var isPreExistingImage = false
|
||||
|
@ -50,8 +50,8 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
ankiCacheDirectory = context.externalCacheDir?.absolutePath
|
||||
// #9639: .opus is application/octet-stream in API 26,
|
||||
// requires a workaround as we don't want to enable application/octet-stream by default
|
||||
val btnLibrary = Button(mActivity)
|
||||
btnLibrary.text = mActivity.getText(R.string.multimedia_editor_import_audio)
|
||||
val btnLibrary = Button(_activity)
|
||||
btnLibrary.text = _activity.getText(R.string.multimedia_editor_import_audio)
|
||||
btnLibrary.setOnClickListener {
|
||||
openChooserPrompt(
|
||||
"audio/*",
|
||||
@ -60,8 +60,8 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
)
|
||||
}
|
||||
layout.addView(btnLibrary, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
val btnVideo = Button(mActivity).apply {
|
||||
text = mActivity.getText(R.string.multimedia_editor_import_video)
|
||||
val btnVideo = Button(_activity).apply {
|
||||
text = _activity.getText(R.string.multimedia_editor_import_video)
|
||||
setOnClickListener {
|
||||
openChooserPrompt(
|
||||
"video/*",
|
||||
@ -71,11 +71,11 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
}
|
||||
}
|
||||
layout.addView(btnVideo, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
tvAudioClip = FixedTextView(mActivity)
|
||||
if (mField.audioPath == null) {
|
||||
tvAudioClip = FixedTextView(_activity)
|
||||
if (_field.audioPath == null) {
|
||||
tvAudioClip.visibility = View.GONE
|
||||
} else {
|
||||
tvAudioClip.text = mField.audioPath
|
||||
tvAudioClip.text = _field.audioPath
|
||||
tvAudioClip.visibility = View.VISIBLE
|
||||
}
|
||||
layout.addView(tvAudioClip, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
@ -83,7 +83,7 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
private fun openChooserPrompt(initialMimeType: String, extraMimeTypes: Array<String>, @StringRes prompt: Int) {
|
||||
val allowAllFiles =
|
||||
this.mActivity.sharedPrefs().getBoolean("mediaImportAllowAllFiles", false)
|
||||
this._activity.sharedPrefs().getBoolean("mediaImportAllowAllFiles", false)
|
||||
val i = Intent()
|
||||
i.type = if (allowAllFiles) "*/*" else initialMimeType
|
||||
if (!allowAllFiles && extraMimeTypes.any()) {
|
||||
@ -94,17 +94,17 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
i.action = Intent.ACTION_GET_CONTENT
|
||||
// Only get openable files, to avoid virtual files issues with Android 7+
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
val chooserPrompt = mActivity.resources.getString(prompt)
|
||||
val chooserPrompt = _activity.resources.getString(prompt)
|
||||
selectMediaLauncher.launch(Intent.createChooser(i, chooserPrompt))
|
||||
}
|
||||
|
||||
override fun setEditingActivity(activity: MultimediaEditFieldActivity) {
|
||||
super.setEditingActivity(activity)
|
||||
val registry = mActivity.activityResultRegistry
|
||||
val registry = this._activity.activityResultRegistry
|
||||
|
||||
selectMediaLauncher = registry.register(SELECT_MEDIA_LAUNCHER_KEY, ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode != Activity.RESULT_CANCELED) {
|
||||
executeSafe(mActivity, "handleMediaSelection:unhandled") {
|
||||
executeSafe(this._activity, "handleMediaSelection:unhandled") {
|
||||
handleMediaSelection(result.data!!)
|
||||
}
|
||||
}
|
||||
@ -117,7 +117,7 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
// Get information about the selected document
|
||||
val queryColumns = arrayOf(MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.MIME_TYPE)
|
||||
var mediaClipFullNameParts: Array<String>
|
||||
mActivity.contentResolver.query(selectedClip!!, queryColumns, null, null, null).use { cursor ->
|
||||
_activity.contentResolver.query(selectedClip!!, queryColumns, null, null, null).use { cursor ->
|
||||
if (cursor == null) {
|
||||
showThemedToast(
|
||||
AnkiDroidApp.instance.applicationContext,
|
||||
@ -167,12 +167,12 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
|
||||
|
||||
// Copy file contents into new temp file. Possibly check file size first and warn if large?
|
||||
try {
|
||||
mActivity.contentResolver.openInputStream(selectedClip).use { inputStream ->
|
||||
_activity.contentResolver.openInputStream(selectedClip).use { inputStream ->
|
||||
CompatHelper.compat.copyFile(inputStream!!, clipCopy.absolutePath)
|
||||
|
||||
// If everything worked, hand off the information
|
||||
mField.hasTemporaryMedia = true
|
||||
mField.audioPath = clipCopy.absolutePath
|
||||
_field.hasTemporaryMedia = true
|
||||
_field.audioPath = clipCopy.absolutePath
|
||||
tvAudioClip.text = clipCopy.name
|
||||
tvAudioClip.visibility = View.VISIBLE
|
||||
}
|
||||
|
@ -35,16 +35,16 @@ import com.ichi2.ui.FixedEditText
|
||||
* Controllers work with the edit field activity and create UI on it to edit a field.
|
||||
*/
|
||||
class BasicTextFieldController : FieldControllerBase(), IFieldController, DialogInterface.OnClickListener {
|
||||
private lateinit var mEditText: EditText
|
||||
private lateinit var editText: EditText
|
||||
|
||||
// This is used to copy from another field value to this field
|
||||
private lateinit var mPossibleClones: ArrayList<String>
|
||||
private lateinit var possibleClones: ArrayList<String>
|
||||
override fun createUI(context: Context, layout: LinearLayout) {
|
||||
mEditText = FixedEditText(mActivity)
|
||||
mEditText.minLines = 3
|
||||
mEditText.setText(mField.text)
|
||||
layout.addView(mEditText, LinearLayout.LayoutParams.MATCH_PARENT)
|
||||
val layoutTools = LinearLayout(mActivity)
|
||||
editText = FixedEditText(_activity)
|
||||
editText.minLines = 3
|
||||
editText.setText(_field.text)
|
||||
layout.addView(editText, LinearLayout.LayoutParams.MATCH_PARENT)
|
||||
val layoutTools = LinearLayout(_activity)
|
||||
layoutTools.orientation = LinearLayout.HORIZONTAL
|
||||
layout.addView(layoutTools)
|
||||
val p = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1F)
|
||||
@ -53,14 +53,14 @@ class BasicTextFieldController : FieldControllerBase(), IFieldController, Dialog
|
||||
}
|
||||
|
||||
private fun gtxt(id: Int): String {
|
||||
return mActivity.getText(id).toString()
|
||||
return _activity.getText(id).toString()
|
||||
}
|
||||
|
||||
private fun createClearButton(layoutTools: LinearLayout, p: LinearLayout.LayoutParams) {
|
||||
val clearButton = Button(mActivity)
|
||||
val clearButton = Button(_activity)
|
||||
clearButton.text = gtxt(R.string.multimedia_editor_text_field_editing_clear)
|
||||
layoutTools.addView(clearButton, p)
|
||||
clearButton.setOnClickListener { mEditText.setText("") }
|
||||
clearButton.setOnClickListener { editText.setText("") }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,23 +70,23 @@ class BasicTextFieldController : FieldControllerBase(), IFieldController, Dialog
|
||||
*/
|
||||
private fun createCloneButton(layoutTools: LinearLayout, p: LinearLayout.LayoutParams) {
|
||||
// Makes sense only for two and more fields
|
||||
if (mNote.numberOfFields > 1) {
|
||||
if (_note.numberOfFields > 1) {
|
||||
// Should be more than one text not empty fields for clone to make
|
||||
// sense
|
||||
mPossibleClones = ArrayList(mNote.numberOfFields)
|
||||
possibleClones = ArrayList(_note.numberOfFields)
|
||||
var numTextFields = 0
|
||||
for (i in 0 until mNote.numberOfFields) {
|
||||
for (i in 0 until _note.numberOfFields) {
|
||||
// Sort out non text and empty fields
|
||||
val curField = mNote.getField(i) ?: continue
|
||||
val curField = _note.getField(i) ?: continue
|
||||
if (curField.type !== EFieldType.TEXT) {
|
||||
continue
|
||||
}
|
||||
val currFieldText = curField.text ?: continue
|
||||
if (currFieldText.isEmpty() || currFieldText.contentEquals(mField.text)) {
|
||||
if (currFieldText.isEmpty() || currFieldText.contentEquals(_field.text)) {
|
||||
continue
|
||||
}
|
||||
// collect clone sources
|
||||
mPossibleClones.add(currFieldText)
|
||||
possibleClones.add(currFieldText)
|
||||
numTextFields++
|
||||
}
|
||||
|
||||
@ -94,16 +94,16 @@ class BasicTextFieldController : FieldControllerBase(), IFieldController, Dialog
|
||||
if (numTextFields < 1) {
|
||||
return
|
||||
}
|
||||
val btnOtherField = Button(mActivity)
|
||||
val btnOtherField = Button(_activity)
|
||||
btnOtherField.text = gtxt(R.string.multimedia_editor_text_field_editing_clone)
|
||||
layoutTools.addView(btnOtherField, p)
|
||||
val controller = this
|
||||
btnOtherField.setOnClickListener {
|
||||
val fragment = PickStringDialogFragment()
|
||||
fragment.setChoices(mPossibleClones)
|
||||
fragment.setChoices(possibleClones)
|
||||
fragment.setOnclickListener(controller)
|
||||
fragment.setTitle(gtxt(R.string.multimedia_editor_text_field_editing_clone_source))
|
||||
fragment.show(mActivity.supportFragmentManager, "pick.clone")
|
||||
fragment.show(_activity.supportFragmentManager, "pick.clone")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,19 +114,19 @@ class BasicTextFieldController : FieldControllerBase(), IFieldController, Dialog
|
||||
|
||||
// When Done button is clicked
|
||||
override fun onDone() {
|
||||
mField.text = mEditText.text.toString()
|
||||
_field.text = editText.text.toString()
|
||||
}
|
||||
|
||||
// This is when the dialog for clone ends
|
||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||
mEditText.setText(mPossibleClones[which])
|
||||
editText.setText(possibleClones[which])
|
||||
}
|
||||
|
||||
/**
|
||||
* @param text A short cut to show a toast
|
||||
*/
|
||||
private fun showToast(text: CharSequence) {
|
||||
showThemedToast(mActivity, text, true)
|
||||
showThemedToast(_activity, text, true)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -23,33 +23,32 @@ import com.ichi2.anki.multimediacard.IMultimediaEditableNote
|
||||
import com.ichi2.anki.multimediacard.activity.MultimediaEditFieldActivity
|
||||
import com.ichi2.utils.KotlinCleanup
|
||||
|
||||
@KotlinCleanup("remove hungarian notation")
|
||||
@Suppress("VariableNamingDetector")
|
||||
@Suppress("PropertyName") // issues with overriding `setActivity` etc...
|
||||
abstract class FieldControllerBase : IFieldController {
|
||||
@KotlinCleanup("transform mActivity into a property")
|
||||
protected lateinit var mActivity: MultimediaEditFieldActivity
|
||||
protected lateinit var mField: IField
|
||||
protected lateinit var mNote: IMultimediaEditableNote
|
||||
protected var mIndex = 0
|
||||
@KotlinCleanup("transform into a property")
|
||||
protected lateinit var _activity: MultimediaEditFieldActivity
|
||||
protected lateinit var _field: IField
|
||||
protected lateinit var _note: IMultimediaEditableNote
|
||||
protected var index = 0
|
||||
|
||||
override fun setField(field: IField) {
|
||||
mField = field
|
||||
this._field = field
|
||||
}
|
||||
|
||||
override fun setNote(note: IMultimediaEditableNote) {
|
||||
mNote = note
|
||||
this._note = note
|
||||
}
|
||||
|
||||
override fun setFieldIndex(index: Int) {
|
||||
mIndex = index
|
||||
this.index = index
|
||||
}
|
||||
|
||||
override fun setEditingActivity(activity: MultimediaEditFieldActivity) {
|
||||
mActivity = activity
|
||||
this._activity = activity
|
||||
}
|
||||
|
||||
fun getActivity(): MultimediaEditFieldActivity {
|
||||
return mActivity
|
||||
return _activity
|
||||
}
|
||||
|
||||
override fun loadInstanceState(savedInstanceState: Bundle?) { /* Default implementation does nothing */
|
||||
|
@ -33,14 +33,14 @@ import java.util.*
|
||||
class MultimediaEditableNote : IMultimediaEditableNote {
|
||||
override var isModified = false
|
||||
private set
|
||||
private var mFields: ArrayList<IField?>? = null
|
||||
private var fields: ArrayList<IField?>? = null
|
||||
var modelId: NoteTypeId = 0
|
||||
|
||||
/**
|
||||
* Field values in the note editor, before any editing has taken place
|
||||
* These values should not be modified
|
||||
*/
|
||||
private var mInitialFields: ArrayList<IField?>? = null
|
||||
private var initialFields: ArrayList<IField?>? = null
|
||||
private fun setThisModified() {
|
||||
isModified = true
|
||||
}
|
||||
@ -55,10 +55,10 @@ class MultimediaEditableNote : IMultimediaEditableNote {
|
||||
|
||||
private val fieldsPrivate: ArrayList<IField?>
|
||||
get() {
|
||||
if (mFields == null) {
|
||||
mFields = ArrayList(0)
|
||||
if (fields == null) {
|
||||
fields = ArrayList(0)
|
||||
}
|
||||
return mFields!!
|
||||
return fields!!
|
||||
}
|
||||
override val numberOfFields: Int
|
||||
get() = fieldsPrivate.size
|
||||
@ -88,17 +88,17 @@ class MultimediaEditableNote : IMultimediaEditableNote {
|
||||
}
|
||||
|
||||
fun freezeInitialFieldValues() {
|
||||
mInitialFields = ArrayList()
|
||||
for (f in mFields!!) {
|
||||
mInitialFields!!.add(cloneField(f))
|
||||
initialFields = ArrayList()
|
||||
for (f in fields!!) {
|
||||
initialFields!!.add(cloneField(f))
|
||||
}
|
||||
}
|
||||
|
||||
override val initialFieldCount: Int
|
||||
get() = mInitialFields!!.size
|
||||
get() = initialFields!!.size
|
||||
|
||||
override fun getInitialField(index: Int): IField? {
|
||||
return cloneField(mInitialFields!![index])
|
||||
return cloneField(initialFields!![index])
|
||||
}
|
||||
|
||||
private fun cloneField(f: IField?): IField? {
|
||||
|
@ -46,34 +46,34 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
|
||||
/** The non-collapsed subset of the deck tree that matches the current search. */
|
||||
private var filteredDeckList: List<DeckNode> = ArrayList()
|
||||
private val mZeroCountColor: Int
|
||||
private val mNewCountColor: Int
|
||||
private val mLearnCountColor: Int
|
||||
private val mReviewCountColor: Int
|
||||
private val mRowCurrentDrawable: Int
|
||||
private val mDeckNameDefaultColor: Int
|
||||
private val mDeckNameDynColor: Int
|
||||
private val mExpandImage: Drawable?
|
||||
private val mCollapseImage: Drawable?
|
||||
private val zeroCountColor: Int
|
||||
private val newCountColor: Int
|
||||
private val learnCountColor: Int
|
||||
private val reviewCountColor: Int
|
||||
private val rowCurrentDrawable: Int
|
||||
private val deckNameDefaultColor: Int
|
||||
private val deckNameDynColor: Int
|
||||
private val expandImage: Drawable?
|
||||
private val collapseImage: Drawable?
|
||||
private var currentDeckId: DeckId = 0
|
||||
|
||||
// Listeners
|
||||
private var mDeckClickListener: View.OnClickListener? = null
|
||||
private var mDeckExpanderClickListener: View.OnClickListener? = null
|
||||
private var mDeckLongClickListener: OnLongClickListener? = null
|
||||
private var mCountsClickListener: View.OnClickListener? = null
|
||||
private var deckClickListener: View.OnClickListener? = null
|
||||
private var deckExpanderClickListener: View.OnClickListener? = null
|
||||
private var deckLongClickListener: OnLongClickListener? = null
|
||||
private var countsClickListener: View.OnClickListener? = null
|
||||
|
||||
// Totals accumulated as each deck is processed
|
||||
private var mNew = 0
|
||||
private var mLrn = 0
|
||||
private var mRev = 0
|
||||
private var mNumbersComputed = false
|
||||
private var new = 0
|
||||
private var lrn = 0
|
||||
private var rev = 0
|
||||
private var numbersComputed = false
|
||||
|
||||
// Flags
|
||||
private var mHasSubdecks = false
|
||||
private var hasSubdecks = false
|
||||
|
||||
// Whether we have a background (so some items should be partially transparent).
|
||||
private var mPartiallyTransparentForBackground = false
|
||||
private var partiallyTransparentForBackground = false
|
||||
|
||||
// ViewHolder class to save inflated views for recycling
|
||||
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
|
||||
@ -99,24 +99,24 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
}
|
||||
|
||||
fun setDeckClickListener(listener: View.OnClickListener?) {
|
||||
mDeckClickListener = listener
|
||||
deckClickListener = listener
|
||||
}
|
||||
|
||||
fun setCountsClickListener(listener: View.OnClickListener?) {
|
||||
mCountsClickListener = listener
|
||||
countsClickListener = listener
|
||||
}
|
||||
|
||||
fun setDeckExpanderClickListener(listener: View.OnClickListener?) {
|
||||
mDeckExpanderClickListener = listener
|
||||
deckExpanderClickListener = listener
|
||||
}
|
||||
|
||||
fun setDeckLongClickListener(listener: OnLongClickListener?) {
|
||||
mDeckLongClickListener = listener
|
||||
deckLongClickListener = listener
|
||||
}
|
||||
|
||||
/** Sets whether the control should have partial transparency to allow a background to be seen */
|
||||
fun enablePartialTransparencyForBackground(isTransparent: Boolean) {
|
||||
mPartiallyTransparentForBackground = isTransparent
|
||||
partiallyTransparentForBackground = isTransparent
|
||||
}
|
||||
|
||||
private val mutex = Mutex()
|
||||
@ -131,12 +131,12 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
// and do I/O inside it. Better to calculate the new lists outside the lock, then swap
|
||||
mutex.withLock {
|
||||
deckTree = node
|
||||
mHasSubdecks = node.children.any { it.children.any() }
|
||||
hasSubdecks = node.children.any { it.children.any() }
|
||||
currentDeckId = withCol { decks.current().optLong("id") }
|
||||
mRev = node.revCount
|
||||
mLrn = node.lrnCount
|
||||
mNew = node.newCount
|
||||
mNumbersComputed = true
|
||||
rev = node.revCount
|
||||
lrn = node.lrnCount
|
||||
new = node.newCount
|
||||
numbersComputed = true
|
||||
// Filtering performs notifyDataSetChanged after the async work is complete
|
||||
getFilter()?.filter(filter)
|
||||
}
|
||||
@ -159,7 +159,7 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
// Set the expander icon and padding according to whether or not there are any subdecks
|
||||
val deckLayout = holder.deckLayout
|
||||
val rightPadding = deckLayout.resources.getDimension(R.dimen.deck_picker_right_padding).toInt()
|
||||
if (mHasSubdecks) {
|
||||
if (hasSubdecks) {
|
||||
val smallPadding = deckLayout.resources.getDimension(R.dimen.deck_picker_left_padding_small).toInt()
|
||||
deckLayout.setPadding(smallPadding, 0, rightPadding, 0)
|
||||
holder.deckExpander.visibility = View.VISIBLE
|
||||
@ -172,16 +172,16 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
}
|
||||
if (node.children.isNotEmpty()) {
|
||||
holder.deckExpander.tag = node.did
|
||||
holder.deckExpander.setOnClickListener(mDeckExpanderClickListener)
|
||||
holder.deckExpander.setOnClickListener(deckExpanderClickListener)
|
||||
} else {
|
||||
holder.deckExpander.isClickable = false
|
||||
holder.deckExpander.setOnClickListener(null)
|
||||
}
|
||||
holder.deckLayout.setBackgroundResource(mRowCurrentDrawable)
|
||||
holder.deckLayout.setBackgroundResource(rowCurrentDrawable)
|
||||
// Set background colour. The current deck has its own color
|
||||
if (isCurrentlySelectedDeck(node)) {
|
||||
holder.deckLayout.setBackgroundResource(mRowCurrentDrawable)
|
||||
if (mPartiallyTransparentForBackground) {
|
||||
holder.deckLayout.setBackgroundResource(rowCurrentDrawable)
|
||||
if (partiallyTransparentForBackground) {
|
||||
setBackgroundAlpha(holder.deckLayout, SELECTED_DECK_ALPHA_AGAINST_BACKGROUND)
|
||||
}
|
||||
} else {
|
||||
@ -196,27 +196,27 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
val filtered =
|
||||
node.filtered
|
||||
if (filtered) {
|
||||
holder.deckName.setTextColor(mDeckNameDynColor)
|
||||
holder.deckName.setTextColor(deckNameDynColor)
|
||||
} else {
|
||||
holder.deckName.setTextColor(mDeckNameDefaultColor)
|
||||
holder.deckName.setTextColor(deckNameDefaultColor)
|
||||
}
|
||||
|
||||
// Set the card counts and their colors
|
||||
holder.deckNew.text = node.newCount.toString()
|
||||
holder.deckNew.setTextColor(if (node.newCount == 0) mZeroCountColor else mNewCountColor)
|
||||
holder.deckNew.setTextColor(if (node.newCount == 0) zeroCountColor else newCountColor)
|
||||
holder.deckLearn.text = node.lrnCount.toString()
|
||||
holder.deckLearn.setTextColor(if (node.lrnCount == 0) mZeroCountColor else mLearnCountColor)
|
||||
holder.deckLearn.setTextColor(if (node.lrnCount == 0) zeroCountColor else learnCountColor)
|
||||
holder.deckRev.text = node.revCount.toString()
|
||||
holder.deckRev.setTextColor(if (node.revCount == 0) mZeroCountColor else mReviewCountColor)
|
||||
holder.deckRev.setTextColor(if (node.revCount == 0) zeroCountColor else reviewCountColor)
|
||||
|
||||
// Store deck ID in layout's tag for easy retrieval in our click listeners
|
||||
holder.deckLayout.tag = node.did
|
||||
holder.countsLayout.tag = node.did
|
||||
|
||||
// Set click listeners
|
||||
holder.deckLayout.setOnClickListener(mDeckClickListener)
|
||||
holder.deckLayout.setOnLongClickListener(mDeckLongClickListener)
|
||||
holder.countsLayout.setOnClickListener(mCountsClickListener)
|
||||
holder.deckLayout.setOnClickListener(deckClickListener)
|
||||
holder.deckLayout.setOnLongClickListener(deckLongClickListener)
|
||||
holder.countsLayout.setOnClickListener(countsClickListener)
|
||||
}
|
||||
|
||||
private fun setBackgroundAlpha(view: View, alphaPercentage: Double) {
|
||||
@ -239,10 +239,10 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
if (node.children.isNotEmpty()) {
|
||||
expander.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
if (node.collapsed) {
|
||||
expander.setImageDrawable(mExpandImage)
|
||||
expander.setImageDrawable(expandImage)
|
||||
expander.contentDescription = expander.context.getString(R.string.expand)
|
||||
} else {
|
||||
expander.setImageDrawable(mCollapseImage)
|
||||
expander.setImageDrawable(collapseImage)
|
||||
expander.contentDescription = expander.context.getString(R.string.collapse)
|
||||
}
|
||||
} else {
|
||||
@ -275,8 +275,8 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
}
|
||||
|
||||
val due: Int?
|
||||
get() = if (mNumbersComputed) {
|
||||
mNew + mLrn + mRev
|
||||
get() = if (numbersComputed) {
|
||||
new + lrn + rev
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@ -324,17 +324,17 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context)
|
||||
R.attr.collapseRef
|
||||
)
|
||||
val ta = context.obtainStyledAttributes(attrs)
|
||||
mZeroCountColor = ta.getColor(0, context.getColor(R.color.black))
|
||||
mNewCountColor = ta.getColor(1, context.getColor(R.color.black))
|
||||
mLearnCountColor = ta.getColor(2, context.getColor(R.color.black))
|
||||
mReviewCountColor = ta.getColor(3, context.getColor(R.color.black))
|
||||
mRowCurrentDrawable = ta.getResourceId(4, 0)
|
||||
mDeckNameDefaultColor = ta.getColor(5, context.getColor(R.color.black))
|
||||
mDeckNameDynColor = ta.getColor(6, context.getColor(R.color.material_blue_A700))
|
||||
mExpandImage = ta.getDrawable(7)
|
||||
mExpandImage!!.isAutoMirrored = true
|
||||
mCollapseImage = ta.getDrawable(8)
|
||||
mCollapseImage!!.isAutoMirrored = true
|
||||
zeroCountColor = ta.getColor(0, context.getColor(R.color.black))
|
||||
newCountColor = ta.getColor(1, context.getColor(R.color.black))
|
||||
learnCountColor = ta.getColor(2, context.getColor(R.color.black))
|
||||
reviewCountColor = ta.getColor(3, context.getColor(R.color.black))
|
||||
rowCurrentDrawable = ta.getResourceId(4, 0)
|
||||
deckNameDefaultColor = ta.getColor(5, context.getColor(R.color.black))
|
||||
deckNameDynColor = ta.getColor(6, context.getColor(R.color.material_blue_A700))
|
||||
expandImage = ta.getDrawable(7)
|
||||
expandImage!!.isAutoMirrored = true
|
||||
collapseImage = ta.getDrawable(8)
|
||||
collapseImage!!.isAutoMirrored = true
|
||||
ta.recycle()
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ fun DeckPicker.performAsyncOperation(
|
||||
operation: AsyncOperation,
|
||||
channel: Channel
|
||||
) {
|
||||
if (mActivityPaused) {
|
||||
if (activityPaused) {
|
||||
sendNotificationForAsyncOperation(operation, channel)
|
||||
return
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class AudioRecordingController :
|
||||
override fun createUI(context: Context, layout: LinearLayout) {
|
||||
audioRecorder = AudioRecorder()
|
||||
if (inEditField) {
|
||||
val origAudioPath = this.mField.audioPath
|
||||
val origAudioPath = this._field.audioPath
|
||||
var bExist = false
|
||||
if (origAudioPath != null) {
|
||||
val f = File(origAudioPath)
|
||||
@ -96,7 +96,7 @@ class AudioRecordingController :
|
||||
}
|
||||
}
|
||||
if (!bExist) {
|
||||
tempAudioPath = generateTempAudioFile(mActivity)
|
||||
tempAudioPath = generateTempAudioFile(_activity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,8 +121,8 @@ class AudioRecordingController :
|
||||
label.gravity = Gravity.CENTER_HORIZONTAL
|
||||
previewLayout.addView(label)
|
||||
var hasTextContents = false
|
||||
for (i in 0 until mNote.initialFieldCount) {
|
||||
val field = mNote.getInitialField(i)
|
||||
for (i in 0 until _note.initialFieldCount) {
|
||||
val field = _note.getInitialField(i)
|
||||
FixedTextView(this).apply {
|
||||
text = field?.text
|
||||
textSize = 16f
|
||||
@ -355,8 +355,8 @@ class AudioRecordingController :
|
||||
}
|
||||
|
||||
private fun saveRecording() {
|
||||
mField.audioPath = tempAudioPath
|
||||
mField.hasTemporaryMedia = true
|
||||
_field.audioPath = tempAudioPath
|
||||
_field.hasTemporaryMedia = true
|
||||
}
|
||||
|
||||
fun stopAndSaveRecording() {
|
||||
|
@ -460,7 +460,7 @@ open class Card : Cloneable {
|
||||
open class Cache : Cloneable {
|
||||
val col: Collection
|
||||
val id: Long
|
||||
private var mCard: Card? = null
|
||||
private var _card: Card? = null
|
||||
|
||||
constructor(col: Collection, id: Long) {
|
||||
this.col = col
|
||||
@ -471,7 +471,7 @@ open class Card : Cloneable {
|
||||
protected constructor(cache: Cache) {
|
||||
col = cache.col
|
||||
this.id = cache.id
|
||||
mCard = cache.mCard
|
||||
_card = cache._card
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,16 +481,16 @@ open class Card : Cloneable {
|
||||
@get:Synchronized
|
||||
val card: Card
|
||||
get() {
|
||||
if (mCard == null) {
|
||||
mCard = col.getCard(this.id)
|
||||
if (_card == null) {
|
||||
_card = col.getCard(this.id)
|
||||
}
|
||||
return mCard!!
|
||||
return _card!!
|
||||
}
|
||||
|
||||
/** Next access to card will reload the card from the database. */
|
||||
@Synchronized
|
||||
open fun reload() {
|
||||
mCard = null
|
||||
_card = null
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
@ -109,8 +109,8 @@ open class Collection(
|
||||
lateinit var sched: Scheduler
|
||||
protected set
|
||||
|
||||
private var mStartTime: Long
|
||||
private var mStartReps: Int
|
||||
private var startTime: Long
|
||||
private var startReps: Int
|
||||
|
||||
val mod: Long
|
||||
get() = db.queryLongScalar("select mod from col")
|
||||
@ -131,15 +131,15 @@ open class Collection(
|
||||
var ls: Long = 0
|
||||
// END: SQL table columns
|
||||
|
||||
private var mLogHnd: PrintWriter? = null
|
||||
private var logHnd: PrintWriter? = null
|
||||
|
||||
init {
|
||||
media = Media(this)
|
||||
tags = Tags(this)
|
||||
val created = reopen()
|
||||
log(path, VersionUtils.pkgVersionName)
|
||||
mStartReps = 0
|
||||
mStartTime = 0
|
||||
startReps = 0
|
||||
startTime = 0
|
||||
_loadScheduler()
|
||||
if (created) {
|
||||
sched.useNewTimezoneCode()
|
||||
@ -457,8 +457,8 @@ open class Collection(
|
||||
*/
|
||||
|
||||
fun startTimebox() {
|
||||
mStartTime = TimeManager.time.intTime()
|
||||
mStartReps = sched.reps
|
||||
startTime = TimeManager.time.intTime()
|
||||
startReps = sched.reps
|
||||
}
|
||||
|
||||
data class TimeboxReached(val secs: Int, val reps: Int)
|
||||
@ -470,12 +470,12 @@ open class Collection(
|
||||
// timeboxing disabled
|
||||
return null
|
||||
}
|
||||
val elapsed = TimeManager.time.intTime() - mStartTime
|
||||
val elapsed = TimeManager.time.intTime() - startTime
|
||||
val limit = sched.timeboxSecs()
|
||||
return if (elapsed > limit) {
|
||||
TimeboxReached(
|
||||
limit,
|
||||
sched.reps - mStartReps
|
||||
sched.reps - startReps
|
||||
).also {
|
||||
startTimebox()
|
||||
}
|
||||
@ -592,7 +592,7 @@ open class Collection(
|
||||
}
|
||||
|
||||
private fun writeLog(s: String) {
|
||||
mLogHnd?.let {
|
||||
logHnd?.let {
|
||||
try {
|
||||
it.println(s)
|
||||
} catch (e: Exception) {
|
||||
@ -616,7 +616,7 @@ open class Collection(
|
||||
}
|
||||
lpath.renameTo(lpath2)
|
||||
}
|
||||
mLogHnd = PrintWriter(BufferedWriter(FileWriter(lpath, true)), true)
|
||||
logHnd = PrintWriter(BufferedWriter(FileWriter(lpath, true)), true)
|
||||
} catch (e: IOException) {
|
||||
// turn off logging if we can't open the log file
|
||||
Timber.e("Failed to open collection.log file - disabling logging")
|
||||
@ -627,8 +627,8 @@ open class Collection(
|
||||
private fun _closeLog() {
|
||||
if (!debugLog) return
|
||||
Timber.i("Closing Collection Log")
|
||||
mLogHnd?.close()
|
||||
mLogHnd = null
|
||||
logHnd?.close()
|
||||
logHnd = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,11 +48,12 @@ import java.util.*
|
||||
* This technique can be used with an [android.app.Activity] class, not just
|
||||
* [android.preference.PreferenceActivity].
|
||||
*/
|
||||
@KotlinCleanup("replace _delegate with `field`")
|
||||
abstract class AppCompatPreferenceActivity<PreferenceHack : AppCompatPreferenceActivity<PreferenceHack>.AbstractPreferenceHack> :
|
||||
PreferenceActivity(),
|
||||
CoroutineScope by MainScope(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private var mDelegate: AppCompatDelegate? = null
|
||||
private var _delegate: AppCompatDelegate? = null
|
||||
fun isColInitialized() = ::col.isInitialized
|
||||
protected var prefChanged = false
|
||||
lateinit var unmountReceiver: BroadcastReceiver
|
||||
@ -264,10 +265,10 @@ abstract class AppCompatPreferenceActivity<PreferenceHack : AppCompatPreferenceA
|
||||
|
||||
private val delegate: AppCompatDelegate
|
||||
get() {
|
||||
if (mDelegate == null) {
|
||||
mDelegate = AppCompatDelegate.create(this, null)
|
||||
if (_delegate == null) {
|
||||
_delegate = AppCompatDelegate.create(this, null)
|
||||
}
|
||||
return mDelegate!! // safe as mDelegate is only initialized here, before being returned
|
||||
return _delegate!! // safe as mDelegate is only initialized here, before being returned
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -281,7 +281,7 @@ class AbstractFlashcardViewerTest : RobolectricTest() {
|
||||
controller.pause()
|
||||
assertThat("disabled after pause", viewer.automaticAnswer.isDisabled, equalTo(true))
|
||||
assertThat("no auto answer after pause", viewer.hasAutomaticAnswerQueued(), equalTo(false))
|
||||
viewer.mOnRenderProcessGoneDelegate.onRenderProcessGone(viewer.webView!!, mock(RenderProcessGoneDetail::class.java))
|
||||
viewer.onRenderProcessGoneDelegate.onRenderProcessGone(viewer.webView!!, mock(RenderProcessGoneDetail::class.java))
|
||||
assertThat("no auto answer after onRenderProcessGone when paused", viewer.hasAutomaticAnswerQueued(), equalTo(false))
|
||||
}
|
||||
|
||||
|
@ -691,7 +691,7 @@ class DeckPickerTest : RobolectricTest() {
|
||||
assertThat("unbury is not visible: deck has no cards", !col.sched.haveBuriedInCurrentDeck())
|
||||
|
||||
deckPicker {
|
||||
assertThat("deck focus is set", mFocusedDeck, equalTo(emptyDeck))
|
||||
assertThat("deck focus is set", focusedDeck, equalTo(emptyDeck))
|
||||
|
||||
// ACT: open up the Deck Context Menu
|
||||
val deckToClick = recyclerView.children.single {
|
||||
@ -701,7 +701,7 @@ class DeckPickerTest : RobolectricTest() {
|
||||
|
||||
// ASSERT
|
||||
assertThat("unbury is visible: one card is buried", col.sched.haveBuriedInCurrentDeck())
|
||||
assertThat("deck focus has changed", mFocusedDeck, equalTo(deckWithCards))
|
||||
assertThat("deck focus has changed", focusedDeck, equalTo(deckWithCards))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,7 +412,7 @@ class ReviewerKeyboardInputTest : RobolectricTest() {
|
||||
fun displayingAnswer(): KeyboardInputTestReviewer {
|
||||
val keyboardInputTestReviewer = KeyboardInputTestReviewer()
|
||||
displayAnswer = true
|
||||
keyboardInputTestReviewer.mProcessor.setup()
|
||||
keyboardInputTestReviewer.processor.setup()
|
||||
return keyboardInputTestReviewer
|
||||
}
|
||||
|
||||
@ -420,7 +420,7 @@ class ReviewerKeyboardInputTest : RobolectricTest() {
|
||||
fun displayingQuestion(): KeyboardInputTestReviewer {
|
||||
val keyboardInputTestReviewer = KeyboardInputTestReviewer()
|
||||
displayAnswer = false
|
||||
keyboardInputTestReviewer.mProcessor.setup()
|
||||
keyboardInputTestReviewer.processor.setup()
|
||||
return keyboardInputTestReviewer
|
||||
}
|
||||
}
|
||||
|
@ -419,4 +419,4 @@ class ReviewerTest : RobolectricTest() {
|
||||
}
|
||||
}
|
||||
|
||||
val Reviewer.isDisplayingMark: Boolean get() = this.mCardMarker!!.isDisplayingMark
|
||||
val Reviewer.isDisplayingMark: Boolean get() = this.cardMarker!!.isDisplayingMark
|
||||
|
@ -46,8 +46,8 @@ import java.util.*
|
||||
*/
|
||||
@Suppress("unused")
|
||||
public class AddContentApi(context: Context) {
|
||||
private val mContext: Context = context.applicationContext
|
||||
private val mResolver: ContentResolver = mContext.contentResolver
|
||||
private val context: Context = context.applicationContext
|
||||
private val resolver: ContentResolver = this.context.contentResolver
|
||||
|
||||
/**
|
||||
* Create a new note with specified fields, tags, and model and place it in the specified deck.
|
||||
@ -78,16 +78,16 @@ public class AddContentApi(context: Context) {
|
||||
}
|
||||
|
||||
private fun addNoteForContentValues(deckId: Long, values: ContentValues): Uri? {
|
||||
val newNoteUri = mResolver.insert(Note.CONTENT_URI, values) ?: return null
|
||||
val newNoteUri = resolver.insert(Note.CONTENT_URI, values) ?: return null
|
||||
// Move cards to specified deck
|
||||
val cardsUri = Uri.withAppendedPath(newNoteUri, "cards")
|
||||
val cardsQuery = mResolver.query(cardsUri, null, null, null, null) ?: return null
|
||||
val cardsQuery = resolver.query(cardsUri, null, null, null, null) ?: return null
|
||||
cardsQuery.use { cardsCursor ->
|
||||
while (cardsCursor.moveToNext()) {
|
||||
val ord = cardsCursor.getString(cardsCursor.getColumnIndex(Card.CARD_ORD))
|
||||
val cardValues = ContentValues().apply { put(Card.DECK_ID, deckId) }
|
||||
val cardUri = Uri.withAppendedPath(Uri.withAppendedPath(newNoteUri, "cards"), ord)
|
||||
mResolver.update(cardUri, cardValues, null, null)
|
||||
resolver.update(cardUri, cardValues, null, null)
|
||||
}
|
||||
}
|
||||
return newNoteUri
|
||||
@ -167,7 +167,7 @@ public class AddContentApi(context: Context) {
|
||||
put(AnkiMedia.PREFERRED_NAME, preferredName.replace(" ", "_"))
|
||||
}
|
||||
return try {
|
||||
val returnUri = mResolver.insert(AnkiMedia.CONTENT_URI, contentValues)
|
||||
val returnUri = resolver.insert(AnkiMedia.CONTENT_URI, contentValues)
|
||||
// get the filename from Uri, return [sound:%s] % file.getName()
|
||||
val fname = File(returnUri!!.path!!).toString()
|
||||
formatMediaName(fname, mimeType)
|
||||
@ -244,7 +244,7 @@ public class AddContentApi(context: Context) {
|
||||
*/
|
||||
public fun getNote(noteId: Long): NoteInfo? {
|
||||
val noteUri = Uri.withAppendedPath(Note.CONTENT_URI, noteId.toString())
|
||||
val query = mResolver.query(noteUri, PROJECTION, null, null, null) ?: return null
|
||||
val query = resolver.query(noteUri, PROJECTION, null, null, null) ?: return null
|
||||
return query.use { cursor ->
|
||||
if (!cursor.moveToNext()) {
|
||||
null
|
||||
@ -260,7 +260,7 @@ public class AddContentApi(context: Context) {
|
||||
if (fields != null) put(Note.FLDS, Utils.joinFields(fields))
|
||||
if (tags != null) put(Note.TAGS, Utils.joinTags(tags))
|
||||
}
|
||||
val numRowsUpdated = mResolver.update(contentUri, values, null, null)
|
||||
val numRowsUpdated = resolver.update(contentUri, values, null, null)
|
||||
// provider doesn't check whether fields actually changed, so just returns number of notes with id == noteId
|
||||
return numRowsUpdated > 0
|
||||
}
|
||||
@ -281,7 +281,7 @@ public class AddContentApi(context: Context) {
|
||||
// Build map of HTML for each generated card
|
||||
val cards: MutableMap<String, Map<String, String>> = HashMap()
|
||||
val cardsUri = Uri.withAppendedPath(newNoteUri, "cards")
|
||||
val cardsQuery = mResolver.query(cardsUri, null, null, null, null) ?: return null
|
||||
val cardsQuery = resolver.query(cardsUri, null, null, null, null) ?: return null
|
||||
cardsQuery.use { cardsCursor ->
|
||||
while (cardsCursor.moveToNext()) {
|
||||
// add question and answer for each card to map
|
||||
@ -295,7 +295,7 @@ public class AddContentApi(context: Context) {
|
||||
}
|
||||
}
|
||||
// Delete the note
|
||||
mResolver.delete(newNoteUri!!, null, null)
|
||||
resolver.delete(newNoteUri!!, null, null)
|
||||
return cards
|
||||
}
|
||||
|
||||
@ -371,7 +371,7 @@ public class AddContentApi(context: Context) {
|
||||
put(Model.DECK_ID, did)
|
||||
put(Model.SORT_FIELD_INDEX, sortf)
|
||||
}
|
||||
val modelUri = mResolver.insert(Model.CONTENT_URI, values) ?: return null
|
||||
val modelUri = resolver.insert(Model.CONTENT_URI, values) ?: return null
|
||||
// Set the remaining template parameters
|
||||
val templatesUri = Uri.withAppendedPath(modelUri, "templates")
|
||||
for (i in cards.indices) {
|
||||
@ -382,7 +382,7 @@ public class AddContentApi(context: Context) {
|
||||
put(CardTemplate.ANSWER_FORMAT, afmt[i])
|
||||
put(CardTemplate.ANSWER_FORMAT, afmt[i])
|
||||
}
|
||||
mResolver.update(uri, values, null, null)
|
||||
resolver.update(uri, values, null, null)
|
||||
}
|
||||
return modelUri.lastPathSegment!!.toLong()
|
||||
} // Get the current model
|
||||
@ -395,7 +395,7 @@ public class AddContentApi(context: Context) {
|
||||
get() {
|
||||
// Get the current model
|
||||
val uri = Uri.withAppendedPath(Model.CONTENT_URI, Model.CURRENT_MODEL_ID)
|
||||
val singleModelQuery = mResolver.query(uri, null, null, null, null) ?: return -1L
|
||||
val singleModelQuery = resolver.query(uri, null, null, null, null) ?: return -1L
|
||||
return singleModelQuery.use { singleModelCursor ->
|
||||
singleModelCursor.moveToFirst()
|
||||
singleModelCursor.getLong(singleModelCursor.getColumnIndex(Model._ID))
|
||||
@ -410,7 +410,7 @@ public class AddContentApi(context: Context) {
|
||||
public fun getFieldList(modelId: Long): Array<String>? {
|
||||
// Get the current model
|
||||
val uri = Uri.withAppendedPath(Model.CONTENT_URI, modelId.toString())
|
||||
val modelQuery = mResolver.query(uri, null, null, null, null) ?: return null
|
||||
val modelQuery = resolver.query(uri, null, null, null, null) ?: return null
|
||||
var splitFlds: Array<String>? = null
|
||||
modelQuery.use { modelCursor ->
|
||||
if (modelCursor.moveToNext()) {
|
||||
@ -437,7 +437,7 @@ public class AddContentApi(context: Context) {
|
||||
public fun getModelList(minNumFields: Int): Map<Long, String>? {
|
||||
// Get the current model
|
||||
val allModelsQuery =
|
||||
mResolver.query(Model.CONTENT_URI, null, null, null, null)
|
||||
resolver.query(Model.CONTENT_URI, null, null, null, null)
|
||||
?: return null
|
||||
val models: MutableMap<Long, String> = HashMap()
|
||||
allModelsQuery.use { allModelsCursor ->
|
||||
@ -470,7 +470,7 @@ public class AddContentApi(context: Context) {
|
||||
public fun addNewDeck(deckName: String): Long? {
|
||||
// Create a new note
|
||||
val values = ContentValues().apply { put(Deck.DECK_NAME, deckName) }
|
||||
val newDeckUri = mResolver.insert(Deck.CONTENT_ALL_URI, values)
|
||||
val newDeckUri = resolver.insert(Deck.CONTENT_ALL_URI, values)
|
||||
return if (newDeckUri != null) {
|
||||
newDeckUri.lastPathSegment!!.toLong()
|
||||
} else {
|
||||
@ -484,7 +484,7 @@ public class AddContentApi(context: Context) {
|
||||
*/
|
||||
public val selectedDeckName: String?
|
||||
get() {
|
||||
val selectedDeckQuery = mResolver.query(
|
||||
val selectedDeckQuery = resolver.query(
|
||||
Deck.CONTENT_SELECTED_URI,
|
||||
null,
|
||||
null,
|
||||
@ -508,7 +508,7 @@ public class AddContentApi(context: Context) {
|
||||
get() {
|
||||
// Get the current model
|
||||
val allDecksQuery =
|
||||
mResolver.query(Deck.CONTENT_ALL_URI, null, null, null, null) ?: return null
|
||||
resolver.query(Deck.CONTENT_ALL_URI, null, null, null, null) ?: return null
|
||||
val decks: MutableMap<Long, String> = HashMap()
|
||||
allDecksQuery.use { allDecksCursor ->
|
||||
while (allDecksCursor.moveToNext()) {
|
||||
@ -547,14 +547,14 @@ public class AddContentApi(context: Context) {
|
||||
// GET_META_DATA seems to work anyway
|
||||
val info =
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
mContext.packageManager.resolveContentProvider(
|
||||
context.packageManager.resolveContentProvider(
|
||||
FlashCardsContract.AUTHORITY,
|
||||
PackageManager.ComponentInfoFlags.of(
|
||||
PackageManager.GET_META_DATA.toLong()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
mContext.packageManager.resolveContentProvider(
|
||||
context.packageManager.resolveContentProvider(
|
||||
FlashCardsContract.AUTHORITY,
|
||||
PackageManager.GET_META_DATA
|
||||
)
|
||||
@ -571,7 +571,7 @@ public class AddContentApi(context: Context) {
|
||||
}
|
||||
|
||||
private fun hasReadWritePermission(): Boolean =
|
||||
mContext.checkPermission(
|
||||
context.checkPermission(
|
||||
READ_WRITE_PERMISSION,
|
||||
Process.myPid(),
|
||||
Process.myUid()
|
||||
@ -615,7 +615,7 @@ public class AddContentApi(context: Context) {
|
||||
override fun queryNotes(modelId: Long): Cursor? {
|
||||
val modelName = getModelName(modelId) ?: return null
|
||||
val queryFormat = "note:\"$modelName\""
|
||||
return mResolver.query(
|
||||
return resolver.query(
|
||||
Note.CONTENT_URI,
|
||||
PROJECTION,
|
||||
queryFormat,
|
||||
@ -639,7 +639,7 @@ public class AddContentApi(context: Context) {
|
||||
val queryFormat = "${modelFieldList[0]}:\"%%s\" note:\"$modelName\""
|
||||
for (outputPos in keys.indices) {
|
||||
val selection = String.format(queryFormat, keys[outputPos])
|
||||
val query = mResolver.query(
|
||||
val query = resolver.query(
|
||||
Note.CONTENT_URI,
|
||||
PROJECTION,
|
||||
selection,
|
||||
@ -677,7 +677,7 @@ public class AddContentApi(context: Context) {
|
||||
|
||||
private inner class CompatV2 : CompatV1() {
|
||||
override fun queryNotes(modelId: Long): Cursor? {
|
||||
return mResolver.query(
|
||||
return resolver.query(
|
||||
Note.CONTENT_URI_V2,
|
||||
PROJECTION,
|
||||
String.format(Locale.US, "%s=%d", Note.MID, modelId),
|
||||
@ -691,7 +691,7 @@ public class AddContentApi(context: Context) {
|
||||
Note.DECK_ID_QUERY_PARAM,
|
||||
deckId.toString()
|
||||
)
|
||||
return mResolver.bulkInsert(builder.build(), valuesArr)
|
||||
return resolver.bulkInsert(builder.build(), valuesArr)
|
||||
}
|
||||
|
||||
override fun findDuplicateNotes(
|
||||
@ -718,7 +718,7 @@ public class AddContentApi(context: Context) {
|
||||
Note.CSUM,
|
||||
csums.joinToString(separator = ",")
|
||||
)
|
||||
val notesTableQuery = mResolver.query(
|
||||
val notesTableQuery = resolver.query(
|
||||
Note.CONTENT_URI_V2,
|
||||
PROJECTION,
|
||||
sel,
|
||||
|
Loading…
Reference in New Issue
Block a user