Merge pull request #2854 from kiwix/Issue#2851

Upgrading gradle plugin version to 7.1.0
This commit is contained in:
Kelson 2022-06-20 12:03:01 +02:00 committed by GitHub
commit 9defd1eba8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 438 additions and 366 deletions

View File

@ -24,6 +24,11 @@ jobs:
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: create instrumentation coverage - name: create instrumentation coverage
uses: ReactiveCircus/android-emulator-runner@v2.23.0 uses: ReactiveCircus/android-emulator-runner@v2.23.0
env: env:
@ -34,7 +39,6 @@ jobs:
ndk: 21.4.7075529 ndk: 21.4.7075529
script: bash contrib/instrumentation.sh script: bash contrib/instrumentation.sh
- name: Upload screenshot result - name: Upload screenshot result
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
if: failure() if: failure()
@ -48,8 +52,7 @@ jobs:
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
if: ${{ matrix.api-level==21 }} if: ${{ matrix.api-level==21 }}
run: | uses: codecov/codecov-action@v2
bash <(curl -s https://codecov.io/bash)
- name: Upload Coverage to GH-Actions - name: Upload Coverage to GH-Actions
uses: actions/upload-artifact@v2.2.0 uses: actions/upload-artifact@v2.2.0

View File

@ -44,6 +44,11 @@ jobs:
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Install NDK - name: Install NDK
run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.0.7026061" --sdk_root=${ANDROID_SDK_ROOT} run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.0.7026061" --sdk_root=${ANDROID_SDK_ROOT}
@ -70,4 +75,3 @@ jobs:
mkdir $DATE mkdir $DATE
cp $UNIVERSAL_DEBUG_APK $DATE cp $UNIVERSAL_DEBUG_APK $DATE
scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no $DATE ci@master.download.kiwix.org:/data/download/nightly/ scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no $DATE ci@master.download.kiwix.org:/data/download/nightly/

View File

@ -13,6 +13,11 @@ jobs:
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Static Analysis - name: Static Analysis
run: ./gradlew ktlintCheck detekt app:lintDebug custom:lintCustomexampleDebug run: ./gradlew ktlintCheck detekt app:lintDebug custom:lintCustomexampleDebug
@ -34,6 +39,11 @@ jobs:
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Install NDK - name: Install NDK
run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.0.7026061" --sdk_root=${ANDROID_SDK_ROOT} run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.0.7026061" --sdk_root=${ANDROID_SDK_ROOT}

View File

@ -17,6 +17,11 @@ jobs:
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Decrypt files - name: Decrypt files
env: env:
keystore: ${{ secrets.keystore }} keystore: ${{ secrets.keystore }}

View File

@ -30,8 +30,8 @@ fun generateVersionName() = "${ext["versionMajor"]}.${ext["versionMinor"]}.${ext
fun generateVersionCode() = fun generateVersionCode() =
20 * 10000 + 20 * 10000 +
((ext["versionMajor"] as Int) * 10000) + ext["versionMajor"] as Int * 10000 +
((ext["versionMinor"] as Int) * 100) + ext["versionMinor"] as Int * 100 +
ext["versionPatch"] as Int ext["versionPatch"] as Int
val apkPrefix get() = System.getenv("TAG") ?: "dev" val apkPrefix get() = System.getenv("TAG") ?: "dev"
@ -45,7 +45,6 @@ android {
versionCode = generateVersionCode() versionCode = generateVersionCode()
versionName = generateVersionName() versionName = generateVersionName()
} }
lintOptions { lintOptions {
isCheckDependencies = true isCheckDependencies = true
} }
@ -78,11 +77,11 @@ android {
} }
play { play {
isEnabled = true enabled.set(true)
serviceAccountCredentials = file("../google.json") serviceAccountCredentials.set(file("../google.json"))
track = "alpha" track.set("alpha")
releaseStatus = "draft" releaseStatus.set(com.github.triplet.gradle.androidpublisher.ReleaseStatus.DRAFT)
resolutionStrategy = "fail" resolutionStrategy.set(com.github.triplet.gradle.androidpublisher.ResolutionStrategy.FAIL)
} }
dependencies { dependencies {

View File

@ -1,19 +1,19 @@
<?xml version="1.0" ?> <?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline> <SmellBaseline>
<Blacklist></Blacklist> <ManuallySuppressedIssues/>
<Whitelist> <CurrentIssues>
<ID>EmptyFunctionBlock:None.kt$None${ }</ID> <ID>EmptyFunctionBlock:None.kt$None${ }</ID>
<ID>EmptyFunctionBlock:SimplePageChangeListener.kt$SimplePageChangeListener${ }</ID> <ID>EmptyFunctionBlock:SimplePageChangeListener.kt$SimplePageChangeListener${ }</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List&lt;BookOnDisk&gt;, activeDownloads: List&lt;DownloadModel&gt;, allLanguages: List&lt;Language&gt;, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState )</ID> <ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List&lt;BookOnDisk>, activeDownloads: List&lt;DownloadModel>, allLanguages: List&lt;Language>, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState )</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: FetchDownloadDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private val kiwixService: KiwixService, private val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil )</ID> <ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: FetchDownloadDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private val kiwixService: KiwixService, private val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil )</ID>
<ID>MagicNumber:LibraryListItem.kt$LibraryListItem.LibraryDownloadItem$1000L</ID> <ID>MagicNumber:LibraryListItem.kt$LibraryListItem.LibraryDownloadItem$1000L</ID>
<ID>MagicNumber:PeerGroupHandshake.kt$PeerGroupHandshake$15000</ID> <ID>MagicNumber:PeerGroupHandshake.kt$PeerGroupHandshake$15000</ID>
<ID>MagicNumber:ShareFiles.kt$ShareFiles$24</ID> <ID>MagicNumber:ShareFiles.kt$ShareFiles$24</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$5</ID> <ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$5</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$500</ID> <ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$500</ID>
<ID>NestedBlockDepth:LocalLibraryFragment.kt$LocalLibraryFragment$checkPermissions</ID> <ID>NestedBlockDepth:LocalLibraryFragment.kt$LocalLibraryFragment$private fun checkPermissions()</ID>
<ID>NestedBlockDepth:PeerGroupHandshake.kt$PeerGroupHandshake$readHandshakeAndExchangeMetaData</ID> <ID>NestedBlockDepth:PeerGroupHandshake.kt$PeerGroupHandshake$private fun readHandshakeAndExchangeMetaData(): InetAddress?</ID>
<ID>NestedBlockDepth:ReceiverHandShake.kt$ReceiverHandShake$exchangeFileTransferMetadata</ID> <ID>NestedBlockDepth:ReceiverHandShake.kt$ReceiverHandShake$override fun exchangeFileTransferMetadata(inputStream: InputStream, outputStream: OutputStream)</ID>
<ID>PackageNaming:AvailableSpaceCalculator.kt$package org.kiwix.kiwixmobile.zim_manager.library_view</ID> <ID>PackageNaming:AvailableSpaceCalculator.kt$package org.kiwix.kiwixmobile.zim_manager.library_view</ID>
<ID>PackageNaming:ConnectivityBroadcastReceiver.kt$package org.kiwix.kiwixmobile.zim_manager</ID> <ID>PackageNaming:ConnectivityBroadcastReceiver.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:DefaultLanguageProvider.kt$package org.kiwix.kiwixmobile.zim_manager</ID> <ID>PackageNaming:DefaultLanguageProvider.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
@ -46,5 +46,5 @@
<ID>TooGenericExceptionThrown:LibraryViewHolder.kt$LibraryViewHolder.LibraryBookViewHolder$throw RuntimeException("impossible invalid state: ${item.fileSystemState}")</ID> <ID>TooGenericExceptionThrown:LibraryViewHolder.kt$LibraryViewHolder.LibraryBookViewHolder$throw RuntimeException("impossible invalid state: ${item.fileSystemState}")</ID>
<ID>TooGenericExceptionThrown:ZimManageViewModel.kt$ZimManageViewModel$throw RuntimeException("Impossible state")</ID> <ID>TooGenericExceptionThrown:ZimManageViewModel.kt$ZimManageViewModel$throw RuntimeException("Impossible state")</ID>
<ID>VariableNaming:PeerGroupHandshake.kt$PeerGroupHandshake$private val HANDSHAKE_MESSAGE = "Request Kiwix File Sharing"</ID> <ID>VariableNaming:PeerGroupHandshake.kt$PeerGroupHandshake$private val HANDSHAKE_MESSAGE = "Request Kiwix File Sharing"</ID>
</Whitelist> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View File

@ -108,4 +108,4 @@ private fun resourceId(view: View) =
if (view.id > 0 && view.resources != null) " id:${view.resources.getResourceName(view.id)}" if (view.id > 0 && view.resources != null) " id:${view.resources.getResourceName(view.id)}"
else "" else ""
private fun numSpaces(marginOffset: Int) = (0..marginOffset).fold("", { acc, _ -> "$acc-" }) private fun numSpaces(marginOffset: Int) = (0..marginOffset).fold("") { acc, _ -> "$acc-" }

View File

@ -48,7 +48,7 @@ abstract class BaseActivityTest {
getInstrumentation().targetContext.applicationContext getInstrumentation().targetContext.applicationContext
} }
inline fun <reified T : Activity> activityTestRule( protected inline fun <reified T : Activity> activityTestRule(
noinline beforeActivityAction: (() -> Unit)? = null noinline beforeActivityAction: (() -> Unit)? = null
) = ) =
object : ActivityTestRule<T>(T::class.java) { object : ActivityTestRule<T>(T::class.java) {

View File

@ -40,8 +40,8 @@ class Matcher {
public override fun matchesSafely(view: View): Boolean { public override fun matchesSafely(view: View): Boolean {
val parent = view.parent val parent = view.parent
return parent is ViewGroup && parentMatcher.matches(parent) && view == parent.getChildAt( return parent is ViewGroup && parentMatcher.matches(parent) && view == parent.getChildAt(
position position
) )
} }
} }
} }

View File

@ -32,7 +32,8 @@
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/KiwixTheme.Launcher" android:theme="@style/KiwixTheme.Launcher"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan"
android:exported="true">
<meta-data <meta-data
android:name="android.app.shortcuts" android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" /> android:resource="@xml/shortcuts" />
@ -145,7 +146,8 @@
<service android:name=".webserver.wifi_hotspot.HotspotService" /> <service android:name=".webserver.wifi_hotspot.HotspotService" />
<receiver android:name=".main.KiwixSearchWidget"> <receiver android:name=".main.KiwixSearchWidget"
android:exported="true">
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/kiwix_widget_provider_info" /> android:resource="@xml/kiwix_widget_provider_info" />

View File

@ -72,14 +72,18 @@ class IntroFragment : BaseFragment(), IntroContract.View, FragmentActivityExtens
addOnPageChangeListener(SimplePageChangeListener(::updateView, ::handleDraggingState)) addOnPageChangeListener(SimplePageChangeListener(::updateView, ::handleDraggingState))
} }
tab_indicator.setViewPager(view_pager) tab_indicator.setViewPager(view_pager)
timer?.schedule(object : TimerTask() { timer?.schedule(
override fun run() { object : TimerTask() {
handler.post { override fun run() {
if (currentPage == views.size) currentPage = 0 handler.post {
view_pager.setCurrentItem(currentPage++, true) if (currentPage == views.size) currentPage = 0
view_pager.setCurrentItem(currentPage++, true)
}
} }
} },
}, timerDelay, timerPeriod) timerDelay,
timerPeriod
)
views.forEach { views.forEach {
it.setOnClickListener { dismissAutoRotate() } it.setOnClickListener { dismissAutoRotate() }
} }

View File

@ -125,9 +125,11 @@ class LanguageFragment : BaseFragment() {
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_language, menu) inflater.inflate(R.menu.menu_language, menu)
val search = menu.findItem(R.id.menu_language_search) val search = menu.findItem(R.id.menu_language_search)
(search.actionView as SearchView).setOnQueryTextListener(SimpleTextListener { (search.actionView as SearchView).setOnQueryTextListener(
languageViewModel.actions.offer(Filter(it)) SimpleTextListener {
}) languageViewModel.actions.offer(Filter(it))
}
)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@ -82,7 +82,8 @@ import javax.inject.Inject
const val URIS_KEY = "uris" const val URIS_KEY = "uris"
@SuppressLint("GoogleAppIndexingApiWarning", "Registered") @SuppressLint("GoogleAppIndexingApiWarning", "Registered")
class LocalFileTransferFragment : BaseFragment(), class LocalFileTransferFragment :
BaseFragment(),
WifiDirectManager.Callbacks { WifiDirectManager.Callbacks {
@Inject @Inject
lateinit var alertDialogShower: AlertDialogShower lateinit var alertDialogShower: AlertDialogShower

View File

@ -66,7 +66,8 @@ abstract class PeerGroupHandshake(private var groupInfo: WifiP2pInfo) {
InetSocketAddress( InetSocketAddress(
groupInfo.groupOwnerAddress.hostAddress, groupInfo.groupOwnerAddress.hostAddress,
PEER_HANDSHAKE_PORT PEER_HANDSHAKE_PORT
), 15000 ),
15000
) )
val objectOutputStream = ObjectOutputStream(client.getOutputStream()) val objectOutputStream = ObjectOutputStream(client.getOutputStream())
// Send message for the peer device to verify // Send message for the peer device to verify

View File

@ -112,16 +112,19 @@ class WifiDirectManager @Inject constructor(
private fun unregisterWifiDirectBroadcastReceiver() = context.unregisterReceiver(receiver) private fun unregisterWifiDirectBroadcastReceiver() = context.unregisterReceiver(receiver)
fun discoverPeerDevices() { fun discoverPeerDevices() {
manager?.discoverPeers(channel, object : ActionListener { manager?.discoverPeers(
override fun onSuccess() { channel,
context.toast(R.string.discovery_initiated, Toast.LENGTH_SHORT) object : ActionListener {
} override fun onSuccess() {
context.toast(R.string.discovery_initiated, Toast.LENGTH_SHORT)
}
override fun onFailure(reason: Int) { override fun onFailure(reason: Int) {
Log.d(TAG, "${context.getString(R.string.discovery_failed)}: ${getErrorMessage(reason)}") Log.d(TAG, "${context.getString(R.string.discovery_failed)}: ${getErrorMessage(reason)}")
context.toast(R.string.discovery_failed, Toast.LENGTH_SHORT) context.toast(R.string.discovery_failed, Toast.LENGTH_SHORT)
}
} }
}) )
} }
/* From KiwixWifiP2pBroadcastReceiver.P2pEventListener callback-interface*/ /* From KiwixWifiP2pBroadcastReceiver.P2pEventListener callback-interface*/
@ -188,7 +191,8 @@ class WifiDirectManager @Inject constructor(
hasSenderStartedConnection = true hasSenderStartedConnection = true
connect(senderSelectedPeerDevice) connect(senderSelectedPeerDevice)
context.toast(R.string.performing_handshake, Toast.LENGTH_LONG) context.toast(R.string.performing_handshake, Toast.LENGTH_LONG)
}) }
)
} }
} }
@ -197,17 +201,21 @@ class WifiDirectManager @Inject constructor(
deviceAddress = senderSelectedPeerDevice.deviceAddress deviceAddress = senderSelectedPeerDevice.deviceAddress
wps.setup = WpsInfo.PBC wps.setup = WpsInfo.PBC
} }
manager?.connect(channel, config, object : ActionListener { manager?.connect(
override fun onSuccess() { channel,
// UI updated from broadcast receiver config,
} object : ActionListener {
override fun onSuccess() {
// UI updated from broadcast receiver
}
override fun onFailure(reason: Int) { override fun onFailure(reason: Int) {
val errorMessage = getErrorMessage(reason) val errorMessage = getErrorMessage(reason)
Log.d(TAG, context.getString(R.string.connection_failed) + ": " + errorMessage) Log.d(TAG, context.getString(R.string.connection_failed) + ": " + errorMessage)
context.toast(R.string.connection_failed, Toast.LENGTH_LONG) context.toast(R.string.connection_failed, Toast.LENGTH_LONG)
}
} }
}) )
} }
private fun performHandshakeWith(groupInfo: WifiP2pInfo) { private fun performHandshakeWith(groupInfo: WifiP2pInfo) {
@ -291,17 +299,20 @@ class WifiDirectManager @Inject constructor(
} }
private fun disconnect() { private fun disconnect() {
manager?.removeGroup(channel, object : ActionListener { manager?.removeGroup(
override fun onFailure(reasonCode: Int) { channel,
Log.d(TAG, "Disconnect failed. Reason: $reasonCode") object : ActionListener {
closeChannel() override fun onFailure(reasonCode: Int) {
} Log.d(TAG, "Disconnect failed. Reason: $reasonCode")
closeChannel()
}
override fun onSuccess() { override fun onSuccess() {
Log.d(TAG, "Disconnect successful") Log.d(TAG, "Disconnect successful")
closeChannel() closeChannel()
}
} }
}) )
} }
private fun closeChannel() { private fun closeChannel() {

View File

@ -94,10 +94,12 @@ class LocalLibraryFragment : BaseFragment() {
} }
private val bookDelegate: BookOnDiskDelegate.BookDelegate by lazy { private val bookDelegate: BookOnDiskDelegate.BookDelegate by lazy {
BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil, BookOnDiskDelegate.BookDelegate(
sharedPreferenceUtil,
{ offerAction(RequestNavigateTo(it)) }, { offerAction(RequestNavigateTo(it)) },
{ offerAction(RequestMultiSelection(it)) }, { offerAction(RequestMultiSelection(it)) },
{ offerAction(RequestSelect(it)) }) { offerAction(RequestSelect(it)) }
)
} }
private val booksOnDiskAdapter: BooksOnDiskAdapter by lazy { private val booksOnDiskAdapter: BooksOnDiskAdapter by lazy {
BooksOnDiskAdapter(bookDelegate, BookOnDiskDelegate.LanguageDelegate) BooksOnDiskAdapter(bookDelegate, BookOnDiskDelegate.LanguageDelegate)
@ -138,9 +140,9 @@ class LocalLibraryFragment : BaseFragment() {
} }
zimManageViewModel.fileSelectListStates.observe(viewLifecycleOwner, Observer(::render)) zimManageViewModel.fileSelectListStates.observe(viewLifecycleOwner, Observer(::render))
disposable.add(sideEffects()) disposable.add(sideEffects())
zimManageViewModel.deviceListIsRefreshing.observe(viewLifecycleOwner, Observer { zimManageViewModel.deviceListIsRefreshing.observe(viewLifecycleOwner) {
zim_swiperefresh.isRefreshing = it!! zim_swiperefresh.isRefreshing = it!!
}) }
if (savedInstanceState != null && savedInstanceState.getBoolean(WAS_IN_ACTION_MODE)) { if (savedInstanceState != null && savedInstanceState.getBoolean(WAS_IN_ACTION_MODE)) {
zimManageViewModel.fileSelectActions.offer(FileSelectActions.RestartActionMode) zimManageViewModel.fileSelectActions.offer(FileSelectActions.RestartActionMode)
} }

View File

@ -43,6 +43,7 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
@ -107,7 +108,8 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
LibraryDelegate.DownloadDelegate { LibraryDelegate.DownloadDelegate {
dialogShower.show( dialogShower.show(
KiwixDialog.YesNoDialog.StopDownload, KiwixDialog.YesNoDialog.StopDownload,
{ downloader.cancelDownload(it.downloadId) }) { downloader.cancelDownload(it.downloadId) }
)
}, },
LibraryDelegate.DividerDelegate LibraryDelegate.DividerDelegate
) )
@ -153,18 +155,22 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
viewLifecycleOwner, Observer(::onRefreshStateChange) viewLifecycleOwner, Observer(::onRefreshStateChange)
) )
zimManageViewModel.networkStates.observe(viewLifecycleOwner, Observer(::onNetworkStateChange)) zimManageViewModel.networkStates.observe(viewLifecycleOwner, Observer(::onNetworkStateChange))
zimManageViewModel.shouldShowWifiOnlyDialog.observe(viewLifecycleOwner, Observer { zimManageViewModel.shouldShowWifiOnlyDialog.observe(
viewLifecycleOwner
) {
if (it) { if (it) {
showInternetPermissionDialog() showInternetPermissionDialog()
} }
}) }
// hides keyboard when scrolled // hides keyboard when scrolled
libraryList.addOnScrollListener(SimpleRecyclerViewScrollListener { _, newState -> libraryList.addOnScrollListener(
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { SimpleRecyclerViewScrollListener { _, newState ->
libraryList.closeKeyboard() if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
libraryList.closeKeyboard()
}
} }
}) )
allowInternetPermissionButton.setOnClickListener { allowInternetPermissionButton.setOnClickListener {
showInternetPermissionDialog() showInternetPermissionDialog()
@ -405,7 +411,8 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
}) })
return return
} }
else -> availableSpaceCalculator.hasAvailableSpaceFor(item, else -> availableSpaceCalculator.hasAvailableSpaceFor(
item,
{ downloadFile(item.book) }, { downloadFile(item.book) },
{ {
libraryList.snack( libraryList.snack(
@ -415,7 +422,8 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
R.string.download_change_storage, R.string.download_change_storage,
::showStorageSelectDialog ::showStorageSelectDialog
) )
}) }
)
} }
} }
} }

View File

@ -41,8 +41,9 @@ class KiwixPrefsFragment : CorePrefsFragment() {
override fun setStorage() { override fun setStorage() {
findPreference<Preference>(PREF_STORAGE)?.title = getString( findPreference<Preference>(PREF_STORAGE)?.title = getString(
if (sharedPreferenceUtil.prefStorage == internalStorage()?.let if (sharedPreferenceUtil.prefStorage == internalStorage()?.let(
(sharedPreferenceUtil::getPublicDirectoryPath) sharedPreferenceUtil::getPublicDirectoryPath
)
) R.string.internal_storage ) R.string.internal_storage
else R.string.external_storage else R.string.external_storage
) )

View File

@ -17,6 +17,7 @@
*/ */
package org.kiwix.kiwixmobile.webserver.wifi_hotspot package org.kiwix.kiwixmobile.webserver.wifi_hotspot
import android.annotation.SuppressLint
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
@ -38,18 +39,20 @@ class HotspotNotificationManager @Inject constructor(
private fun hotspotNotificationChannel() { private fun hotspotNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(NotificationChannel( notificationManager.createNotificationChannel(
HOTSPOT_SERVICE_CHANNEL_ID, NotificationChannel(
context.getString(R.string.hotspot_service_channel_name), HOTSPOT_SERVICE_CHANNEL_ID,
NotificationManager.IMPORTANCE_DEFAULT context.getString(R.string.hotspot_service_channel_name),
).apply { NotificationManager.IMPORTANCE_DEFAULT
description = context.getString(R.string.hotspot_channel_description) ).apply {
setSound(null, null) description = context.getString(R.string.hotspot_channel_description)
}) setSound(null, null)
}
)
} }
} }
fun buildForegroundNotification(): Notification { @SuppressLint("UnspecifiedImmutableFlag") fun buildForegroundNotification(): Notification {
val contentIntent = NavDeepLinkBuilder(context).setComponentName( val contentIntent = NavDeepLinkBuilder(context).setComponentName(
KiwixMainActivity::class.java KiwixMainActivity::class.java
) )

View File

@ -483,6 +483,7 @@ class ZimManageViewModel @Inject constructor(
oldBookOnDisk.id == newBookOnDisk.id oldBookOnDisk.id == newBookOnDisk.id
} }
newBookOnDisk.apply { isSelected = firstOrNull?.isSelected ?: false } newBookOnDisk.apply { isSelected = firstOrNull?.isSelected ?: false }
}) }
)
} }
} }

View File

@ -61,7 +61,8 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.45" /> app:layout_constraintVertical_bias="0.45"
tools:ignore="RequiredSize" />
<Button <Button
android:id="@+id/allowInternetPermissionButton" android:id="@+id/allowInternetPermissionButton"

View File

@ -64,6 +64,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:ignore="RequiredSize"
app:layout_constraintVertical_bias="0.45" /> app:layout_constraintVertical_bias="0.45" />
<Button <Button

View File

@ -12,12 +12,13 @@ repositories {
} }
dependencies { dependencies {
implementation("com.android.tools.build:gradle:4.0.1") implementation("com.android.tools.build:gradle:7.1.0")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21")
implementation("com.hiya:jacoco-android:0.2") implementation("com.dicedmelon.gradle:jacoco-android:0.1.5")
implementation("org.jlleitschuh.gradle:ktlint-gradle:9.2.1") implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev129-1.25.0") implementation("com.google.apis:google-api-services-androidpublisher:v3-rev129-1.25.0")
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.9.1") implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.20.0")
implementation("com.googlecode.json-simple:json-simple:1.1")
implementation(gradleApi()) implementation(gradleApi())
implementation(localGroovy()) implementation(localGroovy())

View File

@ -22,7 +22,7 @@ object Versions {
const val com_squareup_okhttp3: String = "4.9.0" const val com_squareup_okhttp3: String = "4.9.0"
const val org_jetbrains_kotlin: String = "1.4.20" const val org_jetbrains_kotlin: String = "1.4.21"
const val androidx_navigation: String = "2.3.1" const val androidx_navigation: String = "2.3.1"
@ -42,11 +42,11 @@ object Versions {
const val android_arch_lifecycle_extensions: String = "1.1.1" const val android_arch_lifecycle_extensions: String = "1.1.1"
const val com_android_tools_build_gradle: String = "4.0.1" // available: "4.1.1" const val com_android_tools_build_gradle: String = "7.1.0"
const val de_fayard_buildsrcversions_gradle_plugin: String = "0.7.0" const val de_fayard_buildsrcversions_gradle_plugin: String = "0.7.0"
const val com_github_triplet_play_gradle_plugin: String = "2.8.0" // available: "3.0.0" const val com_github_triplet_play_gradle_plugin: String = "3.7.0"
const val javax_annotation_api: String = "1.3.2" const val javax_annotation_api: String = "1.3.2"

View File

@ -23,8 +23,6 @@ import Libs
import com.android.build.gradle.BaseExtension import com.android.build.gradle.BaseExtension
import io.gitlab.arturbosch.detekt.extensions.DetektExtension import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.KotlinClosure1
import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
@ -39,7 +37,7 @@ class AllProjectConfigurer {
target.plugins.apply("kotlin-android") target.plugins.apply("kotlin-android")
target.plugins.apply("kotlin-android-extensions") target.plugins.apply("kotlin-android-extensions")
target.plugins.apply("kotlin-kapt") target.plugins.apply("kotlin-kapt")
target.plugins.apply("com.hiya.jacoco-android") target.plugins.apply("com.dicedmelon.gradle.jacoco-android")
target.plugins.apply("org.jlleitschuh.gradle.ktlint") target.plugins.apply("org.jlleitschuh.gradle.ktlint")
target.plugins.apply("io.gitlab.arturbosch.detekt") target.plugins.apply("io.gitlab.arturbosch.detekt")
target.plugins.apply("androidx.navigation.safeargs") target.plugins.apply("androidx.navigation.safeargs")
@ -75,19 +73,21 @@ class AllProjectConfigurer {
execution = "ANDROIDX_TEST_ORCHESTRATOR" execution = "ANDROIDX_TEST_ORCHESTRATOR"
unitTests.apply { unitTests.apply {
isReturnDefaultValues = true isReturnDefaultValues = true
all(KotlinClosure1<Any, Test>({ all {
(this as Test).also { testTask -> it.also { testTask ->
testTask.useJUnitPlatform() testTask.useJUnitPlatform()
testTask.testLogging { testTask.testLogging {
setEvents(setOf("passed", "skipped", "failed", "standardOut", "standardError")) setEvents(setOf("passed", "skipped", "failed", "standardOut", "standardError"))
outputs.upToDateWhen { false } testTask.outputs.upToDateWhen { false }
showStandardStreams = true showStandardStreams = true
} }
testTask.extensions testTask.extensions
.getByType(JacocoTaskExtension::class.java) .getByType(JacocoTaskExtension::class.java).apply {
.isIncludeNoLocationClasses = true isIncludeNoLocationClasses = true
excludes = listOf("jdk.internal.*")
}
} }
}, this)) }
} }
} }
@ -104,16 +104,18 @@ class AllProjectConfigurer {
"CheckResult", "CheckResult",
"LabelFor", "LabelFor",
"LogConditional", "LogConditional",
"ConvertToWebp" "ConvertToWebp",
) //TODO remove this when we remove jcenter from gradle
"JcenterRepositoryObsolete",
warning(
"UnknownNullness", "UnknownNullness",
"SelectableText", "SelectableText",
"MissingTranslation", "MissingTranslation",
"IconDensities", "IconDensities",
"ContentDescription", "ContentDescription",
"IconDipSize" "IconDipSize",
"UnusedResources",
"NonConstantResourceId",
"NotifyDataSetChanged"
) )
lintConfig = target.rootProject.file("lintConfig.xml") lintConfig = target.rootProject.file("lintConfig.xml")
} }
@ -144,9 +146,11 @@ class AllProjectConfigurer {
configureExtension<JacocoPluginExtension> { toolVersion = "0.8.7" } configureExtension<JacocoPluginExtension> { toolVersion = "0.8.7" }
configureExtension<KtlintExtension> { android.set(true) } configureExtension<KtlintExtension> { android.set(true) }
configureExtension<DetektExtension> { configureExtension<DetektExtension> {
buildUponDefaultConfig = true
allRules = false
config = target.files("${target.rootDir}/config/detekt/detekt.yml")
baseline = project.file("detekt_baseline.xml") baseline = project.file("detekt_baseline.xml")
} }
} }
} }

View File

@ -2,10 +2,10 @@ build:
maxIssues: 0 maxIssues: 0
excludeCorrectable: false excludeCorrectable: false
weights: weights:
# complexity: 2 # complexity: 2
# LongParameterList: 1 # LongParameterList: 1
# style: 1 # style: 1
# comments: 1 # comments: 1
config: config:
validation: true validation: true
@ -25,11 +25,11 @@ processors:
console-reports: console-reports:
active: true active: true
exclude: exclude:
- 'ProjectStatisticsReport' - 'ProjectStatisticsReport'
- 'ComplexityReport' - 'ComplexityReport'
- 'NotificationReport' - 'NotificationReport'
# - 'FindingsReport' # - 'FindingsReport'
- 'FileBasedFindingsReport' - 'FileBasedFindingsReport'
comments: comments:
active: true active: true
@ -180,130 +180,22 @@ exceptions:
active: true active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
exceptionNames: exceptionNames:
- ArrayIndexOutOfBoundsException - ArrayIndexOutOfBoundsException
- Error - Error
- Exception - Exception
- IllegalMonitorStateException - IllegalMonitorStateException
- NullPointerException - NullPointerException
- IndexOutOfBoundsException - IndexOutOfBoundsException
- RuntimeException - RuntimeException
- Throwable - Throwable
allowedExceptionNameRegex: "^(_|(ignore|expected).*)" allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
TooGenericExceptionThrown: TooGenericExceptionThrown:
active: true active: true
exceptionNames: exceptionNames:
- Error - Error
- Exception - Exception
- Throwable - Throwable
- RuntimeException - RuntimeException
formatting:
active: true
android: false
autoCorrect: true
AnnotationOnSeparateLine:
active: false
autoCorrect: true
ChainWrapping:
active: true
autoCorrect: true
CommentSpacing:
active: true
autoCorrect: true
EnumEntryNameCase:
active: false
autoCorrect: true
Filename:
active: true
FinalNewline:
active: true
autoCorrect: true
ImportOrdering:
active: false
autoCorrect: true
Indentation:
active: false
autoCorrect: true
indentSize: 4
continuationIndentSize: 4
MaximumLineLength:
active: true
maxLineLength: 120
ModifierOrdering:
active: true
autoCorrect: true
MultiLineIfElse:
active: true
autoCorrect: true
NoBlankLineBeforeRbrace:
active: true
autoCorrect: true
NoConsecutiveBlankLines:
active: true
autoCorrect: true
NoEmptyClassBody:
active: true
autoCorrect: true
NoEmptyFirstLineInMethodBlock:
active: false
autoCorrect: true
NoLineBreakAfterElse:
active: true
autoCorrect: true
NoLineBreakBeforeAssignment:
active: true
autoCorrect: true
NoMultipleSpaces:
active: true
autoCorrect: true
NoSemicolons:
active: true
autoCorrect: true
NoTrailingSpaces:
active: true
autoCorrect: true
NoUnitReturn:
active: true
autoCorrect: true
NoUnusedImports:
active: true
autoCorrect: true
NoWildcardImports:
active: true
PackageName:
active: true
autoCorrect: true
ParameterListWrapping:
active: true
autoCorrect: true
indentSize: 4
SpacingAroundColon:
active: true
autoCorrect: true
SpacingAroundComma:
active: true
autoCorrect: true
SpacingAroundCurly:
active: true
autoCorrect: true
SpacingAroundDot:
active: true
autoCorrect: true
SpacingAroundKeyword:
active: true
autoCorrect: true
SpacingAroundOperators:
active: true
autoCorrect: true
SpacingAroundParens:
active: true
autoCorrect: true
SpacingAroundRangeOperator:
active: true
autoCorrect: true
StringTemplate:
active: true
autoCorrect: true
naming: naming:
active: true active: true

View File

@ -19,8 +19,34 @@ plugins {
plugins.apply(KiwixConfigurationPlugin::class) plugins.apply(KiwixConfigurationPlugin::class)
apply(plugin = "io.objectbox") apply(plugin = "io.objectbox")
apply(plugin = "com.jakewharton.butterknife") apply(plugin = "com.jakewharton.butterknife")
ext {
set("versionMajor", 3)
set("versionMinor", 5)
set("versionPatch", 0)
}
/*
* max version code: 21-0-0-00-00-00
* our template : UU-D-A-ZZ-YY-XX
* where:
* X = patch version
* Y = minor version
* Z = major version (+ 20 to distinguish from previous, non semantic, versions of the app)
* A = number representing ABI split
* D = number representing density split
* U = unused
*/
fun generateVersionCode() =
20 * 10000 +
ext["versionMajor"] as Int * 10000 +
ext["versionMinor"] as Int * 100 +
ext["versionPatch"] as Int
android { android {
defaultConfig {
buildConfigField("long", "VERSION_CODE", "${generateVersionCode()}")
}
buildTypes { buildTypes {
getByName("release") { getByName("release") {
isMinifyEnabled = false isMinifyEnabled = false
@ -31,7 +57,6 @@ android {
fun shouldUseLocalVersion() = File(projectDir, "libs").exists() fun shouldUseLocalVersion() = File(projectDir, "libs").exists()
dependencies { dependencies {
// use jdk8 java.time backport, as long app < Build.VERSION_CODES.O // use jdk8 java.time backport, as long app < Build.VERSION_CODES.O
implementation(Libs.threetenabp) implementation(Libs.threetenabp)

View File

@ -1,18 +1,20 @@
<?xml version="1.0" ?> <?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline> <SmellBaseline>
<Blacklist></Blacklist> <ManuallySuppressedIssues/>
<Whitelist> <CurrentIssues>
<ID>EmptyFunctionBlock:BooksOnDiskViewHolder.kt$BookOnDiskViewHolder.BookViewHolder${ }</ID> <ID>EmptyFunctionBlock:BooksOnDiskViewHolder.kt$BookOnDiskViewHolder.BookViewHolder${ }</ID>
<ID>EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.&lt;no name provided&gt;${}</ID> <ID>EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.&lt;no name provided>${}</ID>
<ID>EmptyFunctionBlock:OnSwipeTouchListener.kt$OnSwipeTouchListener${}</ID> <ID>EmptyFunctionBlock:OnSwipeTouchListener.kt$OnSwipeTouchListener${}</ID>
<ID>ForbiddenComment:JNIInitialiser.kt$JNIInitialiser$// TODO: Consider surfacing to user</ID> <ID>ForbiddenComment:JNIInitialiser.kt$JNIInitialiser$// TODO: Consider surfacing to user</ID>
<ID>LongMethod:TabsAdapter.kt$TabsAdapter$onCreateViewHolder</ID> <ID>ForbiddenComment:NetworkUtilsTest.kt$NetworkUtilsTest$// TODO: find a way to assert regex matching via JUnit and rewrite the test</ID>
<ID>LongParameterList:KiwixDialog.kt$KiwixDialog$( val title: Int?, val message: Int?, val positiveMessage: Int, val negativeMessage: Int?, val cancelable: Boolean = true, val icon: Int? = null, val neutralMessage: Int? = null, val getView: (() -&gt; View)? = null )</ID> <ID>LongMethod:ChunkUtilsTest.kt$ChunkUtilsTest$@Test fun testGetChunks()</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView&gt;, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID> <ID>LongMethod:TabsAdapter.kt$TabsAdapter$@SuppressLint("ResourceType") override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ViewHolder</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView&gt;, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID> <ID>LongParameterList:KiwixDialog.kt$KiwixDialog$( val title: Int?, val message: Int?, val positiveMessage: Int, val negativeMessage: Int?, val cancelable: Boolean = true, val icon: Int? = null, val neutralMessage: Int? = null, val getView: (() -> View)? = null )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
<ID>LongParameterList:PageTestHelpers.kt$( bookmarkTitle: String = "bookmarkTitle", isSelected: Boolean = false, id: Long = 2, zimId: String = "zimId", zimName: String = "zimName", zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" )</ID>
<ID>LongParameterList:Repository.kt$Repository$( @param:IO private val io: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val bookmarksDao: NewBookmarksDao, private val historyDao: HistoryDao, private val languageDao: NewLanguagesDao, private val recentSearchDao: NewRecentSearchDao, private val zimReaderContainer: ZimReaderContainer )</ID> <ID>LongParameterList:Repository.kt$Repository$( @param:IO private val io: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val bookmarksDao: NewBookmarksDao, private val historyDao: HistoryDao, private val languageDao: NewLanguagesDao, private val recentSearchDao: NewRecentSearchDao, private val zimReaderContainer: ZimReaderContainer )</ID>
<ID>LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup, videoView: ViewGroup, webViewClient: CoreWebViewClient, private val toolbarView: View, private val bottomBarView: View, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null )</ID> <ID>LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup, videoView: ViewGroup, webViewClient: CoreWebViewClient, private val toolbarView: View, private val bottomBarView: View, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null )</ID>
<ID>MagicNumber:ArticleCount.kt$ArticleCount$1000.0</ID>
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID> <ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
<ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID> <ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID>
<ID>MagicNumber:DownloadItem.kt$DownloadItem$1000L</ID> <ID>MagicNumber:DownloadItem.kt$DownloadItem$1000L</ID>
@ -29,11 +31,15 @@
<ID>MagicNumber:Seconds.kt$Seconds$60</ID> <ID>MagicNumber:Seconds.kt$Seconds$60</ID>
<ID>MagicNumber:Seconds.kt$Seconds$60.0</ID> <ID>MagicNumber:Seconds.kt$Seconds$60.0</ID>
<ID>MagicNumber:TabsAdapter.kt$TabsAdapter$8</ID> <ID>MagicNumber:TabsAdapter.kt$TabsAdapter$8</ID>
<ID>NestedBlockDepth:FileUtils.kt$FileUtils$deleteZimFile</ID> <ID>MatchingDeclarationName:PageTestHelpers.kt$PageImpl : Page</ID>
<ID>NestedBlockDepth:ImageUtils.kt$ImageUtils$getBitmapFromView</ID> <ID>MaxLineLength:BookUtilsTest.kt$BookUtilsTest$// this case uses the result from the container nested class inside LanguageUtils. It will be tested in LanguageUtilsTest</ID>
<ID>NestedBlockDepth:JNIInitialiser.kt$JNIInitialiser$loadICUData</ID> <ID>MaxLineLength:MetaLinkNetworkEntityTest.kt$MetaLinkNetworkEntityTest$"http://www.mirrorservice.org/sites/download.kiwix.org/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim"</ID>
<ID>NestedBlockDepth:OnSwipeTouchListener.kt$OnSwipeTouchListener.GestureListener$onFling</ID> <ID>MaxLineLength:NetworkUtilsTest.kt$NetworkUtilsTest$// Here the Method should return the substring between the first '?' character and the nearest '/' character preceeding it</ID>
<ID>NestedBlockDepth:StorageDeviceUtils.kt$StorageDeviceUtils$canWrite</ID> <ID>NestedBlockDepth:FileUtils.kt$FileUtils$@JvmStatic @Synchronized fun deleteZimFile(path: String)</ID>
<ID>NestedBlockDepth:ImageUtils.kt$ImageUtils$private fun getBitmapFromView(width: Int, height: Int, viewToDrawFrom: View): Bitmap?</ID>
<ID>NestedBlockDepth:JNIInitialiser.kt$JNIInitialiser$private fun loadICUData(context: Context): String?</ID>
<ID>NestedBlockDepth:OnSwipeTouchListener.kt$OnSwipeTouchListener.GestureListener$override fun onFling( e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean</ID>
<ID>NestedBlockDepth:StorageDeviceUtils.kt$StorageDeviceUtils$// Amazingly file.canWrite() does not always return the correct value private fun canWrite(file: File): Boolean</ID>
<ID>PackageNaming:ArticleCount.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view</ID> <ID>PackageNaming:ArticleCount.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view</ID>
<ID>PackageNaming:BookOnDiskDelegate.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter</ID> <ID>PackageNaming:BookOnDiskDelegate.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter</ID>
<ID>PackageNaming:BooksOnDiskAdapter.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter</ID> <ID>PackageNaming:BooksOnDiskAdapter.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter</ID>
@ -45,7 +51,7 @@
<ID>PackageNaming:MountPointProducer.kt$package org.kiwix.kiwixmobile.core.zim_manager</ID> <ID>PackageNaming:MountPointProducer.kt$package org.kiwix.kiwixmobile.core.zim_manager</ID>
<ID>PackageNaming:SelectionMode.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view</ID> <ID>PackageNaming:SelectionMode.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view</ID>
<ID>PackageNaming:TagsView.kt$package org.kiwix.kiwixmobile.core.zim_manager</ID> <ID>PackageNaming:TagsView.kt$package org.kiwix.kiwixmobile.core.zim_manager</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getAllZimParts(book: Book): List&lt;File&gt;</ID> <ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getAllZimParts(book: Book): List&lt;File></ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getLocalFilePathByUri( context: Context, uri: Uri ): String?</ID> <ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getLocalFilePathByUri( context: Context, uri: Uri ): String?</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun hasPart(file: File): Boolean</ID> <ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun hasPart(file: File): Boolean</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@Synchronized private fun deleteZimFileParts(path: String): Boolean</ID> <ID>ReturnCount:FileUtils.kt$FileUtils$@Synchronized private fun deleteZimFileParts(path: String): Boolean</ID>
@ -68,5 +74,5 @@
<ID>TopLevelPropertyNaming:Bytes.kt$const val Mb = Kb * 1024</ID> <ID>TopLevelPropertyNaming:Bytes.kt$const val Mb = Kb * 1024</ID>
<ID>TopLevelPropertyNaming:Bytes.kt$const val Pb = Tb * 1024</ID> <ID>TopLevelPropertyNaming:Bytes.kt$const val Pb = Tb * 1024</ID>
<ID>TopLevelPropertyNaming:Bytes.kt$const val Tb = Gb * 1024</ID> <ID>TopLevelPropertyNaming:Bytes.kt$const val Tb = Gb * 1024</ID>
</Whitelist> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View File

@ -4,7 +4,8 @@
android:installLocation="auto"> android:installLocation="auto">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

View File

@ -22,8 +22,10 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.recyclerview.widget.RecyclerView.ViewHolder
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.extensions.LayoutContainer
abstract class BaseViewHolder<in ITEM>(override val containerView: View) : ViewHolder( abstract class BaseViewHolder<in ITEM>(override val containerView: View) :
containerView ViewHolder(
), LayoutContainer { containerView
),
LayoutContainer {
abstract fun bind(item: ITEM) abstract fun bind(item: ITEM)
} }

View File

@ -30,9 +30,11 @@ import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import javax.inject.Inject import javax.inject.Inject
class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDao { class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDao {
fun bookmarks(): Flowable<List<Page>> = box.asFlowable(box.query { fun bookmarks(): Flowable<List<Page>> = box.asFlowable(
order(BookmarkEntity_.bookmarkTitle) box.query {
}).map { it.map(::BookmarkItem) } order(BookmarkEntity_.bookmarkTitle)
}
).map { it.map(::BookmarkItem) }
override fun pages(): Flowable<List<Page>> = bookmarks() override fun pages(): Flowable<List<Page>> = bookmarks()
override fun deletePages(pagesToDelete: List<Page>) = override fun deletePages(pagesToDelete: List<Page>) =
@ -55,7 +57,8 @@ class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDa
.or() .or()
.equal(BookmarkEntity_.zimName, zimFileReader?.name ?: "") .equal(BookmarkEntity_.zimName, zimFileReader?.name ?: "")
order(BookmarkEntity_.bookmarkTitle) order(BookmarkEntity_.bookmarkTitle)
}).map { it.map(BookmarkEntity::bookmarkUrl) } }
).map { it.map(BookmarkEntity::bookmarkUrl) }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
fun saveBookmark(bookmarkItem: BookmarkItem) { fun saveBookmark(bookmarkItem: BookmarkItem) {

View File

@ -68,7 +68,8 @@ class Repository @Inject internal constructor(
.map { .map {
HeaderizableList<BooksOnDiskListItem, BookOnDisk, LanguageItem>(it).foldOverAddingHeaders( HeaderizableList<BooksOnDiskListItem, BookOnDisk, LanguageItem>(it).foldOverAddingHeaders(
{ bookOnDisk -> LanguageItem(bookOnDisk.locale) }, { bookOnDisk -> LanguageItem(bookOnDisk.locale) },
{ current, next -> current.locale.displayName != next.locale.displayName }) { current, next -> current.locale.displayName != next.locale.displayName }
)
} }
.map { it.toList() } .map { it.toList() }

View File

@ -45,9 +45,11 @@ class NetworkModule {
.connectTimeout(CONNECTION_TIMEOUT, SECONDS) .connectTimeout(CONNECTION_TIMEOUT, SECONDS)
.readTimeout(READ_TIMEOUT, SECONDS) .readTimeout(READ_TIMEOUT, SECONDS)
.callTimeout(CALL_TIMEOUT, SECONDS) .callTimeout(CALL_TIMEOUT, SECONDS)
.addNetworkInterceptor(HttpLoggingInterceptor().apply { .addNetworkInterceptor(
level = if (BuildConfig.DEBUG) BASIC else NONE HttpLoggingInterceptor().apply {
}) level = if (BuildConfig.DEBUG) BASIC else NONE
}
)
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT)) .addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
.build() .build()
} }

View File

@ -17,6 +17,7 @@
*/ */
package org.kiwix.kiwixmobile.core.downloader.fetch package org.kiwix.kiwixmobile.core.downloader.fetch
import android.annotation.SuppressLint
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
@ -107,6 +108,7 @@ class FetchDownloadNotificationManager(context: Context) :
notificationCustomisation(downloadNotification, notificationBuilder, context) notificationCustomisation(downloadNotification, notificationBuilder, context)
} }
@SuppressLint("UnspecifiedImmutableFlag")
private fun notificationCustomisation( private fun notificationCustomisation(
downloadNotification: DownloadNotification, downloadNotification: DownloadNotification,
notificationBuilder: NotificationCompat.Builder, notificationBuilder: NotificationCompat.Builder,

View File

@ -17,6 +17,7 @@
*/ */
package org.kiwix.kiwixmobile.core.error package org.kiwix.kiwixmobile.core.error
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -139,7 +140,7 @@ open class ErrorActivity : BaseActivity() {
All Zim Files in DB: All Zim Files in DB:
$allZimFiles $allZimFiles
""".trimIndent() """.trimIndent()
} }
private fun languageLocale(): String = """ private fun languageLocale(): String = """
@ -184,13 +185,15 @@ open class ErrorActivity : BaseActivity() {
get() = """ get() = """
Hi Kiwix Developers! Hi Kiwix Developers!
The Android app crashed, here are some details to help fix it: The Android app crashed, here are some details to help fix it:
""".trimIndent() """.trimIndent()
private val versionCode: Int private val versionCode: Int
@SuppressLint("WrongConstant")
get() = packageManager get() = packageManager
.getPackageInfo(packageName, ZERO).versionCode .getPackageInfo(packageName, ZERO).versionCode
private val versionName: String private val versionName: String
@SuppressLint("WrongConstant")
get() = packageManager get() = packageManager
.getPackageInfo(packageName, ZERO).versionName .getPackageInfo(packageName, ZERO).versionName

View File

@ -29,11 +29,13 @@ fun Book.calculateSearchMatches(
) { ) {
val searchableText = buildSearchableText(bookUtils) val searchableText = buildSearchableText(bookUtils)
searchMatches = filter.split("\\s+") searchMatches = filter.split("\\s+")
.foldRight(0, .foldRight(
0,
{ filterWord, acc -> { filterWord, acc ->
if (searchableText.contains(filterWord, true)) acc + 1 if (searchableText.contains(filterWord, true)) acc + 1
else acc else acc
}) }
)
} }
fun Book.buildSearchableText(bookUtils: BookUtils): String = fun Book.buildSearchableText(bookUtils: BookUtils): String =

View File

@ -85,10 +85,14 @@ abstract class HelpFragment : BaseFragment() {
private fun sendFeedback() { private fun sendFeedback() {
val intent = Intent(Intent.ACTION_SENDTO) val intent = Intent(Intent.ACTION_SENDTO)
intent.data = ("mailto:${Uri.encode(CONTACT_EMAIL_ADDRESS)}" + intent.data = (
"?subject=${ "mailto:${Uri.encode(CONTACT_EMAIL_ADDRESS)}" +
Uri.encode("Feedback in ${getCurrentLocale(requireActivity()).displayLanguage}") "?subject=" +
}").toUri() Uri.encode(
"Feedback in " +
getCurrentLocale(requireActivity()).displayLanguage
)
).toUri()
startActivity(Intent.createChooser(intent, "Send Feedback via Email")) startActivity(Intent.createChooser(intent, "Send Feedback via Email"))
} }
} }

View File

@ -243,11 +243,13 @@ class AddNoteDialog : DialogFragment() {
// Show the previously saved note if it exists // Show the previously saved note if it exists
displayNote() displayNote()
add_note_edit_text.addTextChangedListener(SimpleTextWatcher { _, _, _, _ -> add_note_edit_text.addTextChangedListener(
noteEdited = true SimpleTextWatcher { _, _, _, _ ->
enableSaveNoteMenuItem() noteEdited = true
enableShareNoteMenuItem() enableSaveNoteMenuItem()
}) enableShareNoteMenuItem()
}
)
if (!noteFileExists) { if (!noteFileExists) {
// Prepare for input in case of empty/new note // Prepare for input in case of empty/new note
add_note_edit_text.requestFocus() add_note_edit_text.requestFocus()

View File

@ -17,6 +17,7 @@
*/ */
package org.kiwix.kiwixmobile.core.main package org.kiwix.kiwixmobile.core.main
import android.annotation.SuppressLint
import android.app.PendingIntent import android.app.PendingIntent
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider import android.appwidget.AppWidgetProvider
@ -46,6 +47,7 @@ abstract class CoreSearchWidget : AppWidgetProvider() {
} }
} }
@SuppressLint("UnspecifiedImmutableFlag")
private fun pendingIntent(context: Context, action: String) = PendingIntent.getActivity( private fun pendingIntent(context: Context, action: String) = PendingIntent.getActivity(
context, context,
(System.currentTimeMillis() % Int.MAX_VALUE).toInt(), (System.currentTimeMillis() % Int.MAX_VALUE).toInt(),

View File

@ -58,16 +58,19 @@ class KiwixTextToSpeech internal constructor(
private val focusLock: Any = Any() private val focusLock: Any = Any()
private val am: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager private val am: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
@JvmField var currentTTSTask: TTSTask? = null @JvmField var currentTTSTask: TTSTask? = null
private val tts: TextToSpeech = TextToSpeech(instance, OnInitListener { status: Int -> private val tts: TextToSpeech = TextToSpeech(
if (status == SUCCESS) { instance,
Log.d(TAG_KIWIX, "TextToSpeech was initialized successfully.") OnInitListener { status: Int ->
this.isInitialized = true if (status == SUCCESS) {
onInitSucceedListener.onInitSucceed() Log.d(TAG_KIWIX, "TextToSpeech was initialized successfully.")
} else { this.isInitialized = true
Log.e(TAG_KIWIX, "Initialization of TextToSpeech Failed!") onInitSucceedListener.onInitSucceed()
context.toast(R.string.texttospeech_initialization_failed, Toast.LENGTH_SHORT) } else {
Log.e(TAG_KIWIX, "Initialization of TextToSpeech Failed!")
context.toast(R.string.texttospeech_initialization_failed, Toast.LENGTH_SHORT)
}
} }
}) )
/** /**
* Returns whether the TTS is initialized. * Returns whether the TTS is initialized.
@ -139,7 +142,7 @@ class KiwixTextToSpeech internal constructor(
Array.prototype.forEach.call(toRemove, function(elem) { Array.prototype.forEach.call(toRemove, function(elem) {
elem.parentElement.removeChild(elem);}); elem.parentElement.removeChild(elem);});
tts.speakAloud(body.innerText); tts.speakAloud(body.innerText);
""".trimIndent() """.trimIndent()
) )
} }

View File

@ -166,9 +166,11 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor(
} }
val fileToSave = sequence { val fileToSave = sequence {
yield(File(root, fileName)) yield(File(root, fileName))
yieldAll(generateSequence(1) { it + 1 }.map { yieldAll(
File(root, fileName.replace(".", "_$it.")) generateSequence(1) { it + 1 }.map {
}) File(root, fileName.replace(".", "_$it."))
}
)
}.first { !it.exists() } }.first { !it.exists() }
val source = Uri.parse(src) val source = Uri.parse(src)
try { try {

View File

@ -102,9 +102,11 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
inflater.inflate(R.menu.menu_page, menu) inflater.inflate(R.menu.menu_page, menu)
val search = menu.findItem(R.id.menu_page_search).actionView as SearchView val search = menu.findItem(R.id.menu_page_search).actionView as SearchView
search.queryHint = searchQueryHint search.queryHint = searchQueryHint
search.setOnQueryTextListener(SimpleTextListener { search.setOnQueryTextListener(
pageViewModel.actions.offer(Action.Filter(it)) SimpleTextListener {
}) pageViewModel.actions.offer(Action.Filter(it))
}
)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -139,11 +141,13 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
pageViewModel.state.observe(viewLifecycleOwner, Observer(::render)) pageViewModel.state.observe(viewLifecycleOwner, Observer(::render))
// hides keyboard when scrolled // hides keyboard when scrolled
recycler_view.addOnScrollListener(SimpleRecyclerViewScrollListener { _, newState -> recycler_view.addOnScrollListener(
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { SimpleRecyclerViewScrollListener { _, newState ->
recycler_view.closeKeyboard() if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
recycler_view.closeKeyboard()
}
} }
}) )
} }
override fun onCreateView( override fun onCreateView(

View File

@ -39,7 +39,9 @@ data class ShowDeleteBookmarksDialog(
@Inject lateinit var dialogShower: DialogShower @Inject lateinit var dialogShower: DialogShower
override fun invokeWith(activity: AppCompatActivity) { override fun invokeWith(activity: AppCompatActivity) {
activity.cachedComponent.inject(this) activity.cachedComponent.inject(this)
dialogShower.show(if (state.isInSelectionState) DeleteSelectedBookmarks else DeleteAllBookmarks, dialogShower.show(
{ effects.offer(DeletePageItems(state, pageDao)) }) if (state.isInSelectionState) DeleteSelectedBookmarks else DeleteAllBookmarks,
{ effects.offer(DeletePageItems(state, pageDao)) }
)
} }
} }

View File

@ -32,8 +32,10 @@ data class HistoryState(
) : PageState<HistoryItem>() { ) : PageState<HistoryItem>() {
override val visiblePageItems: List<HistoryListItem> = override val visiblePageItems: List<HistoryListItem> =
HeaderizableList<HistoryListItem, HistoryItem, DateItem>(filteredPageItems) HeaderizableList<HistoryListItem, HistoryItem, DateItem>(filteredPageItems)
.foldOverAddingHeaders({ historyItem -> DateItem(historyItem.dateString) }, .foldOverAddingHeaders(
{ current, next -> current.dateString != next.dateString }) { historyItem -> DateItem(historyItem.dateString) },
{ current, next -> current.dateString != next.dateString }
)
override fun copyWithNewItems(newItems: List<HistoryItem>): PageState<HistoryItem> = override fun copyWithNewItems(newItems: List<HistoryItem>): PageState<HistoryItem> =
copy(pageItems = (newItems)) copy(pageItems = (newItems))

View File

@ -119,7 +119,8 @@ class SearchFragment : BaseFragment() {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
goBack() goBack()
} }
}) }
)
} }
private fun setupToolbar(view: View) { private fun setupToolbar(view: View) {
@ -149,11 +150,13 @@ class SearchFragment : BaseFragment() {
val searchMenuItem = menu.findItem(R.id.menu_search) val searchMenuItem = menu.findItem(R.id.menu_search)
searchMenuItem.expandActionView() searchMenuItem.expandActionView()
searchView = searchMenuItem.actionView as SearchView searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(SimpleTextListener { searchView.setOnQueryTextListener(
if (it.isNotEmpty()) { SimpleTextListener {
searchViewModel.actions.offer(Filter(it)) if (it.isNotEmpty()) {
searchViewModel.actions.offer(Filter(it))
}
} }
}) )
searchMenuItem.setOnActionExpandListener(object : OnActionExpandListener { searchMenuItem.setOnActionExpandListener(object : OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem) = false override fun onMenuItemActionExpand(item: MenuItem) = false

View File

@ -25,9 +25,11 @@ import androidx.appcompat.R
object DimenUtils { object DimenUtils {
@JvmStatic fun Context.getToolbarHeight(): Int { @JvmStatic fun Context.getToolbarHeight(): Int {
return resources.getDimensionPixelSize(TypedValue().apply { return resources.getDimensionPixelSize(
theme.resolveAttribute(R.attr.actionBarSize, this, true) TypedValue().apply {
}.resourceId) theme.resolveAttribute(R.attr.actionBarSize, this, true)
}.resourceId
)
} }
@JvmStatic fun Activity.getWindowHeight(): Int = @JvmStatic fun Activity.getWindowHeight(): Int =

View File

@ -58,6 +58,7 @@ class ExternalLinkOpener @Inject constructor(
{ {
sharedPreferenceUtil.putPrefExternalLinkPopup(false) sharedPreferenceUtil.putPrefExternalLinkPopup(false)
openLink(intent) openLink(intent)
}) }
)
} }
} }

View File

@ -18,6 +18,7 @@
package org.kiwix.kiwixmobile.core.utils package org.kiwix.kiwixmobile.core.utils
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Typeface import android.graphics.Typeface
@ -54,12 +55,14 @@ class LanguageUtils(private val context: Context) {
MutableList<LanguageContainer> { MutableList<LanguageContainer> {
val localeCollator = val localeCollator =
Collator.getInstance(context.locale).apply { strength = Collator.SECONDARY } Collator.getInstance(context.locale).apply { strength = Collator.SECONDARY }
languageCodesFromAssets.sortWith(Comparator { o1, o2 -> languageCodesFromAssets.sortWith(
localeCollator.compare( Comparator { o1, o2 ->
o1.languageName, localeCollator.compare(
o2.languageName o1.languageName,
) o2.languageName
}) )
}
)
return languageCodesFromAssets return languageCodesFromAssets
} }
@ -80,7 +83,10 @@ class LanguageUtils(private val context: Context) {
// which also sets a Factory on the LayoutInflator, we have to access the private field of the // which also sets a Factory on the LayoutInflator, we have to access the private field of the
// LayoutInflater, that handles this restriction via Java's reflection API // LayoutInflater, that handles this restriction via Java's reflection API
// and make it accessible set it to false again. // and make it accessible set it to false again.
fun changeFont(layoutInflater: LayoutInflater, sharedPreferenceUtil: SharedPreferenceUtil) { @SuppressLint("SoonBlockedPrivateApi") fun changeFont(
layoutInflater: LayoutInflater,
sharedPreferenceUtil: SharedPreferenceUtil
) {
if (!haveToChangeFont(sharedPreferenceUtil)) { if (!haveToChangeFont(sharedPreferenceUtil)) {
return return
@ -187,6 +193,7 @@ class LanguageUtils(private val context: Context) {
} }
} }
@SuppressLint("AppBundleLocaleChanges")
@JvmStatic @JvmStatic
fun handleLocaleChange(context: Context, language: String) { fun handleLocaleChange(context: Context, language: String) {
val locale = val locale =

View File

@ -34,9 +34,11 @@ sealed class KiwixDialog(
val getView: (() -> View)? = null val getView: (() -> View)? = null
) { ) {
data class DeleteZims(override val args: List<Any>) : KiwixDialog( data class DeleteZims(override val args: List<Any>) :
null, R.string.delete_zim_body, R.string.delete, R.string.no KiwixDialog(
), HasBodyFormatArgs { null, R.string.delete_zim_body, R.string.delete, R.string.no
),
HasBodyFormatArgs {
constructor(zimNameList: String) : this(listOf(zimNameList)) constructor(zimNameList: String) : this(listOf(zimNameList))
} }
@ -99,12 +101,14 @@ sealed class KiwixDialog(
cancelable = false cancelable = false
) )
data class ShowHotspotDetails(override val args: List<Any>) : KiwixDialog( data class ShowHotspotDetails(override val args: List<Any>) :
R.string.hotspot_turned_on, KiwixDialog(
R.string.hotspot_details_message, R.string.hotspot_turned_on,
android.R.string.ok, R.string.hotspot_details_message,
null android.R.string.ok,
), HasBodyFormatArgs { null
),
HasBodyFormatArgs {
constructor(wifiConfiguration: WifiConfiguration) : this( constructor(wifiConfiguration: WifiConfiguration) : this(
listOf(wifiConfiguration.SSID, wifiConfiguration.preSharedKey) listOf(wifiConfiguration.SSID, wifiConfiguration.preSharedKey)
) )
@ -125,9 +129,11 @@ sealed class KiwixDialog(
neutralMessage = R.string.hotspot_dialog_neutral_button neutralMessage = R.string.hotspot_dialog_neutral_button
) )
data class FileTransferConfirmation(override val args: List<Any>) : KiwixDialog( data class FileTransferConfirmation(override val args: List<Any>) :
null, R.string.transfer_to, R.string.yes, android.R.string.cancel KiwixDialog(
), HasBodyFormatArgs { null, R.string.transfer_to, R.string.yes, android.R.string.cancel
),
HasBodyFormatArgs {
constructor(selectedPeerDeviceName: String) : this(listOf(selectedPeerDeviceName)) constructor(selectedPeerDeviceName: String) : this(listOf(selectedPeerDeviceName))
} }

View File

@ -36,8 +36,6 @@ import org.kiwix.kiwixmobile.core.extensions.toast
import java.io.BufferedReader import java.io.BufferedReader
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.Exception
import java.util.ArrayList
object FileUtils { object FileUtils {
@ -252,8 +250,10 @@ object FileUtils {
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
@JvmStatic fun getPathFromUri(activity: Activity, data: Intent): String? { @JvmStatic fun getPathFromUri(activity: Activity, data: Intent): String? {
val uri: Uri? = data.data val uri: Uri? = data.data
val takeFlags: Int = data.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION val takeFlags: Int = data.flags and (
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
uri?.let { uri?.let {
activity.grantUriPermission( activity.grantUriPermission(
activity.packageName, it, activity.packageName, it,

View File

@ -27,9 +27,11 @@ inline class KiloByte(private val kilobyteString: String?) {
get() = kilobyteString?.toLongOrNull()?.let { get() = kilobyteString?.toLongOrNull()?.let {
val units = arrayOf("KB", "MB", "GB", "TB") val units = arrayOf("KB", "MB", "GB", "TB")
val conversion = (log10(it.toDouble()) / log10(1024.0)).toInt() val conversion = (log10(it.toDouble()) / log10(1024.0)).toInt()
(DecimalFormat("#,##0.#") (
.format(it / 1024.0.pow(conversion.toDouble())) + DecimalFormat("#,##0.#")
" " + .format(it / 1024.0.pow(conversion.toDouble())) +
units[conversion]) " " +
units[conversion]
)
} ?: "" } ?: ""
} }

View File

@ -34,7 +34,8 @@ inline class ArticleCount(val articleCount: String) {
val units = arrayOf("", "K", "M", "B", "T") val units = arrayOf("", "K", "M", "B", "T")
val conversion = (log10(size.toDouble()) / 3).toInt() val conversion = (log10(size.toDouble()) / 3).toInt()
context.getString( context.getString(
R.string.articleCount, DecimalFormat("#,##0.#") R.string.articleCount,
DecimalFormat("#,##0.#")
.format(size / 1000.0.pow(conversion.toDouble())) + units[conversion] .format(size / 1000.0.pow(conversion.toDouble())) + units[conversion]
) )
} }

View File

@ -82,7 +82,8 @@ class ChunkUtilsTest {
assertEquals( assertEquals(
"verify that the same notificationID is passed on to each chunk", "verify that the same notificationID is passed on to each chunk",
true, listReturned[0].url == url && true,
listReturned[0].url == url &&
listReturned[1].url == url && listReturned[1].url == url &&
listReturned[2].url == url && listReturned[2].url == url &&
listReturned[3].url == url && listReturned[3].url == url &&
@ -92,7 +93,8 @@ class ChunkUtilsTest {
assertEquals( assertEquals(
"verify that the same URL is passed on to each chunk", "verify that the same URL is passed on to each chunk",
true, listReturned[0].notificationID == 56 && true,
listReturned[0].notificationID == 56 &&
listReturned[1].notificationID == 56 && listReturned[1].notificationID == 56 &&
listReturned[2].notificationID == 56 && listReturned[2].notificationID == 56 &&
listReturned[3].notificationID == 56 && listReturned[3].notificationID == 56 &&

View File

@ -200,32 +200,37 @@ class NetworkUtilsTest {
// Using the standard Kiwix Download URLs // Using the standard Kiwix Download URLs
assertEquals( assertEquals(
"URL Parsing", "No Pictures", NetworkUtils.parseURL( "URL Parsing", "No Pictures",
NetworkUtils.parseURL(
context, context,
"http://ftpmirror.your.org/pub/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim" "http://ftpmirror.your.org/pub/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim"
) )
) )
assertEquals( assertEquals(
"URL Parsing", "No Videos", NetworkUtils.parseURL( "URL Parsing", "No Videos",
NetworkUtils.parseURL(
context, context,
"http://www.mirrorservice.org/sites/download.kiwix.org/zim/wikipedia/" + "http://www.mirrorservice.org/sites/download.kiwix.org/zim/wikipedia/" +
"wikipedia_af_all_novid_2016-05.zim" "wikipedia_af_all_novid_2016-05.zim"
) )
) )
assertEquals( assertEquals(
"URL Parsing", "Simple", NetworkUtils.parseURL( "URL Parsing", "Simple",
NetworkUtils.parseURL(
context, context,
"http://download.wikimedia.org/kiwix/zim/wikipedia/wikipedia_af_all_simple_2016-05.zim" "http://download.wikimedia.org/kiwix/zim/wikipedia/wikipedia_af_all_simple_2016-05.zim"
) )
) )
assertEquals( assertEquals(
"URL Parsing", "No Pictures", NetworkUtils.parseURL( "URL Parsing", "No Pictures",
NetworkUtils.parseURL(
context, context,
"http://mirror.netcologne.de/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim" "http://mirror.netcologne.de/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim"
) )
) )
assertEquals( assertEquals(
"URL Parsing", "Simple", NetworkUtils.parseURL( "URL Parsing", "Simple",
NetworkUtils.parseURL(
context, context,
"http://mirror3.kiwix.org/zim/wikipedia/wikipedia_af_all_simple_2016-05.zim" "http://mirror3.kiwix.org/zim/wikipedia/wikipedia_af_all_simple_2016-05.zim"
) )

View File

@ -34,7 +34,7 @@ internal class ServerUtilsTest {
fec0::d8d1:9ff:fe42:160c fec0::d8d1:9ff:fe42:160c
fec0::8d6e:2327:6d9f:ce75 fec0::8d6e:2327:6d9f:ce75
192.168.200.2 192.168.200.2
""".trimIndent() """.trimIndent()
) )
).isEqualTo( ).isEqualTo(
"192.168.232.2" "192.168.232.2"

View File

@ -105,7 +105,8 @@ class FileSearchTest {
val zimFile = File.createTempFile( val zimFile = File.createTempFile(
"fileToFind", "fileToFind",
".zim", ".zim",
File("$tempRoot${File.separator}dir").apply { mkdirs() }) File("$tempRoot${File.separator}dir").apply(File::mkdirs)
)
every { contentResolver.query(any(), any(), any(), any(), any()) } returns null every { contentResolver.query(any(), any(), any(), any(), any()) } returns null
every { storageDevice.name } returns zimFile.parentFile.parent every { storageDevice.name } returns zimFile.parentFile.parent
val fileList = fileSearch.scan() val fileList = fileSearch.scan()

View File

@ -20,7 +20,7 @@ android {
} }
flavorDimensions("default") flavorDimensions("default")
productFlavors { productFlavors.apply {
CustomApps.createDynamically(project.file("src"), this) CustomApps.createDynamically(project.file("src"), this)
all { all {
File("$projectDir/src", "$name/$name.zim").let { File("$projectDir/src", "$name/$name.zim").let {

View File

@ -17,7 +17,8 @@
android:configChanges="orientation|keyboardHidden|screenSize|locale" android:configChanges="orientation|keyboardHidden|screenSize|locale"
android:theme="@style/KiwixTheme.Launcher" android:theme="@style/KiwixTheme.Launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan"
android:exported="true">
<meta-data <meta-data
android:name="android.app.shortcuts" android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" /> android:resource="@xml/shortcuts" />
@ -53,7 +54,8 @@
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name=".main.CustomSearchWidget"> <receiver android:name=".main.CustomSearchWidget"
android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="KiwixSearchWidget.TEXT_CLICKED" /> <action android:name="KiwixSearchWidget.TEXT_CLICKED" />

View File

@ -183,9 +183,11 @@ class CustomReaderFragment : CoreReaderFragment() {
} }
private fun goToSettings() { private fun goToSettings() {
startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { startActivity(
data = Uri.fromParts("package", activity?.packageName, null) Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
}) data = Uri.fromParts("package", activity?.packageName, null)
}
)
} }
private fun readStorageHasBeenPermanentlyDenied(grantResults: IntArray) = private fun readStorageHasBeenPermanentlyDenied(grantResults: IntArray) =

View File

@ -1,6 +1,6 @@
#Tue Feb 25 11:46:45 IST 2020 #Wed Jun 01 10:28:35 IST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip zipStoreBase=GRADLE_USER_HOME

View File

@ -4,7 +4,20 @@
<ignore path="**-qq/**.xml" /> <ignore path="**-qq/**.xml" />
<ignore path="**-iw/**.xml" /> <ignore path="**-iw/**.xml" />
</issue> </issue>
<issue id="LintError">
<ignore regexp=".*BookmarksRobot.kt.*"/>
<ignore regexp=".*DebugFunctions.kt.*"/>
<ignore regexp=".*HistoryRobot.kt.*"/>
<ignore regexp=".*IntroRobot.kt.*"/>
<ignore regexp=".*LanguageRobot.kt.*"/>
<ignore regexp=".*LibraryRobot.kt.*"/>
<ignore regexp=".*LocalFileTransferRobot.kt.*"/>
<ignore regexp=".*OnlineLibraryRobot.kt.*"/>
<ignore regexp=".*ReaderRobot.kt.*"/>
<ignore regexp=".*SettingsRobot.kt.*"/>
<ignore regexp=".*TopLevelDestinationRobot.kt.*"/>
<ignore regexp=".*ZimHostRobot.kt.*"/>
</issue>
<issue id="TypographyEllipsis"> <issue id="TypographyEllipsis">
<ignore path="**-iw/**.xml" /> <ignore path="**-iw/**.xml" />
</issue> </issue>
@ -19,10 +32,11 @@
<issue id="InvalidPackage"> <issue id="InvalidPackage">
<ignore path="**simple-xml-2.7.1.jar" /> <ignore path="**simple-xml-2.7.1.jar" />
<ignore path="**/squidb*.jar" /> <ignore path="**/squidb*.jar" />
<ignore path="**/org.jacoco.agent-0.8.3-runtime.jar" />
</issue> </issue>
<issue id="IconLocation"> <issue id="IconLocation">
<ignore path="src/main/res/drawable/kiwix_icon_with_title.png" /> <ignore path="src/main/res/drawable/kiwix_icon_with_title.png" />
<ignore path="src/main/res/drawable/search_widget_preview.png" /> <ignore path="core/src/main/res/drawable/search_widget_preview.png" />
</issue> </issue>
<issue id="ConvertToWebp"> <issue id="ConvertToWebp">
<ignore path="src/main/res/drawable/search_widget_preview.png" /> <ignore path="src/main/res/drawable/search_widget_preview.png" />