diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8a8066c..0000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -/build -/captures -/native -/android/build -/android/release -/common/build -/desktop/build -/cli/build -.idea/ -.gradle/ -local.properties -*.key -*.aab -*.jar -/html \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d645695..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 2f2aecf..b5ea315 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,2 @@ -## Linux Command Library (Mobile+CLI+Web) +# linux-cli-lib -![Icon](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandLibrary/master/art/web_hi_res_144.png) - -The app currently has **7680** manual pages, **22+** basic categories and a bunch of general terminal tips. It works 100% offline, doesn't need an internet connection and has no tracking software. - -[![Play Store](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandBibliotheca/master/art/play_store_badge.png)](https://play.google.com/store/apps/details?id=com.inspiredandroid.linuxcommandbibliotheca) -[![F-Droid](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandBibliotheca/master/art/fdroid_badge.png)](https://f-droid.org/en/packages/com.inspiredandroid.linuxcommandbibliotheca/) -[![Web](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandBibliotheca/master/art/web_badge.png)](https://linuxcommandlibrary.com) - -### Mobile screenshots - -

- - - - -

- - - -### CLI screenshot - - - -Execute `gradle :cli:buildJar` to create jar file for Linux, Windows and Mac. - -### Content - -#### Categories - -One-liners, System information, System control, Users & Groups, Files & Folders, Input, Printing, JSON, Network, Search & Find, GIT, SSH, Video & Audio, Package manager, Hacking tools, Terminal games, Crypto currencies, VIM Texteditor, Emacs Texteditor, Nano Texteditor, Pico Texteditor, Micro Texteditor - -#### Tips - -Clear and reset the terminal, List of recent commands, Close a frozen window/application, Tab Completion, Temporary aliases, Permanent aliases, Chain commands, Command syntax, Cursor navigation, Redirection, Special characters in commands, View file permissions, Modify file permissions, Set file permissions via binary references - -### CI/CD - -[Github Action](.github/workflows/android.yml) to automatically create a new Github release with APK and JAR and upload an AAB to the Play Store. - -### Tests - -Android Jetpack Compose screen tests: [ComposeTests.kt](android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeTests.kt) - -Android Jetpack Compose deeplinking tests: [ComposeDeeplinkTests.kt](android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt) - -Common code unit tests: [CommonTests.kt](common/src/commonTest/kotlin/CommonTests.kt) - -### Licensing - -The source code is licensed under the Apache 2.0 license and the copyright of the man pages in the `database.db` file are copyrighted by their respective authors. - -### Thanks to - -http://letsgokoyo.com - App Icon - -https://www.commandlinefu.com - Lots of one-liners - -https://icons8.com - Icons - -https://tldr.sh - TLDR diff --git a/android/build.gradle.kts b/android/build.gradle.kts deleted file mode 100644 index 74991c0..0000000 --- a/android/build.gradle.kts +++ /dev/null @@ -1,78 +0,0 @@ -plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.compose.compiler) -} - -group = "com.inspiredandroid.linuxcommandbibliotheca" - -dependencies { - implementation(project(":common")) - implementation(libs.androidx.activity.compose) - implementation(libs.androidx.material) - implementation(libs.androidx.navigation.compose) - implementation(libs.accompanist.appcompat.theme) - implementation(libs.accompanist.systemuicontroller) - implementation(libs.androidx.lifecycle.viewmodel.compose) - implementation(libs.androidx.preference) - implementation(libs.androidx.ui.tooling.preview) - implementation(libs.androidx.material.icons.core) - - implementation(libs.koin.core) - implementation(libs.koin.android) - implementation(libs.koin.androidx.compose) - implementation(libs.androidx.foundation) - implementation(libs.kotlinx.collections.immutable) - - androidTestImplementation(libs.androidx.ui.test.junit4) - debugImplementation(libs.androidx.ui.test.manifest) - debugImplementation(libs.androidx.ui.tooling) -} - -android { - compileSdk = 36 - defaultConfig { - applicationId = "com.inspiredandroid.linuxcommandbibliotheca" - minSdk = 24 - targetSdk = 36 - versionCode = - libs.versions.androidVersionCode - .get() - .toInt() - versionName = libs.versions.appVersion.get() - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - getByName("release") { - isMinifyEnabled = true - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro", - ) - } - getByName("debug") { - isMinifyEnabled = false - } - } - - sourceSets["main"].assets.setSrcDirs(listOf("../assets")) - - buildFeatures { - compose = true - buildConfig = true - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - kotlinOptions { - jvmTarget = "17" - } - - lint { - abortOnError = false - } - namespace = "com.inspiredandroid.linuxcommandbibliotheca" -} diff --git a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt b/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt deleted file mode 100644 index a73e52c..0000000 --- a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.content.Context -import android.content.Intent -import android.net.Uri -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.junit4.createEmptyComposeRule -import androidx.compose.ui.test.onNodeWithContentDescription -import androidx.compose.ui.test.performClick -import androidx.preference.PreferenceManager -import androidx.test.core.app.ActivityScenario -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.linuxcommandlibrary.shared.initDatabase -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Test if deeplinks from/to website open correct screens. If test started the first time - * "Always open urls with app" dialog has to be accepted. Or the app has to be signed with the - * release key for autoVerify to take effect. - */ -@RunWith(AndroidJUnit4::class) -class ComposeDeeplinkTests { - - private lateinit var scenario: ActivityScenario - - @get:Rule - val composeTestRule = createEmptyComposeRule() - - @Before - fun setUp() { - val context: Context = ApplicationProvider.getApplicationContext() - initDatabase(context) - - // Clear bookmarks - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.edit().putString("KEY_BOOKMARKS", "").apply() - } - - private fun openUrl(url: String) { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - scenario = ActivityScenario.launch(intent) - } - - @Test - fun testBasicCategories() { - openUrl("https://linuxcommandlibrary.com/basics") - - composeTestRule.onNodeWithContentDescription("TopAppBarTitle") - .assertTextEquals("Basics") - } - - @Test - fun testBasicCategory() { - openUrl("https://linuxcommandlibrary.com/basic/usersgroups") - - composeTestRule.onNodeWithContentDescription("TopAppBarTitle") - .assertTextEquals("Users & Groups") - } - - @Test - fun testTips() { - openUrl("https://linuxcommandlibrary.com/tips") - - composeTestRule.onNodeWithContentDescription("TopAppBarTitle") - .assertTextEquals("Tips") - } - - @Test - fun testCommandList() { - openUrl("https://linuxcommandlibrary.com/") - - composeTestRule.onNodeWithContentDescription("Back").performClick() - composeTestRule.onNodeWithContentDescription("TopAppBarTitle") - .assertTextEquals("Commands") - } - - @Test - fun testCommandDetail() { - openUrl("https://linuxcommandlibrary.com/man/2048") - - composeTestRule.onNodeWithContentDescription("TopAppBarTitle") - .assertTextEquals("2048") - } -} diff --git a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeScreenshots.kt b/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeScreenshots.kt deleted file mode 100644 index b064178..0000000 --- a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeScreenshots.kt +++ /dev/null @@ -1,138 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.content.Context -import android.graphics.Bitmap -import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES -import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode -import androidx.compose.ui.graphics.asAndroidBitmap -import androidx.compose.ui.test.* -import androidx.compose.ui.test.junit4.ComposeTestRule -import androidx.compose.ui.test.junit4.createEmptyComposeRule -import androidx.preference.PreferenceManager -import androidx.test.core.app.ActivityScenario -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.linuxcommandlibrary.shared.copyDatabase -import com.linuxcommandlibrary.shared.initDatabase -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.koin.java.KoinJavaComponent.inject -import java.io.FileOutputStream - -/** - * Take screenshots of Phone and Tablet. - * Phone = Pixel 2 1080x1920 - * Tablet 7" = Nexus 7 1200x1920 - * - * Pull images from device to art folder for readme: - * run pull_screenshots.sh - */ -@RunWith(AndroidJUnit4::class) -class ComposeScreenshots { - - private lateinit var scenario: ActivityScenario - - @get:Rule - val composeTestRule = createEmptyComposeRule() - - @Before - fun setUp() { - val context: Context = ApplicationProvider.getApplicationContext() - copyDatabase(context) - initDatabase(context) - - val dataManager: com.inspiredandroid.linuxcommandbibliotheca.DataManager by inject(com.inspiredandroid.linuxcommandbibliotheca.DataManager::class.java) - dataManager.updateDatabaseVersion() - - // Clear bookmarks - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.edit().putString("KEY_BOOKMARKS", "").apply() - - // Clear files folder - InstrumentationRegistry.getInstrumentation().targetContext.filesDir.listFiles()?.forEach { - it.deleteRecursively() - } - scenario = ActivityScenario.launch(MainActivity::class.java) - } - - @Test - fun takeTabletLightAndDarkScreenshots() { - takeTabletScreenshots("") - scenario = ActivityScenario.launch(MainActivity::class.java) - scenario.onActivity { - setDefaultNightMode(MODE_NIGHT_YES) - } - takeTabletScreenshots("-dark") - } - - private fun takeTabletScreenshots(prefix: String) { - // Tips - composeTestRule.onNodeWithText("Tips").performClick() - composeTestRule.takeScreenshot("screen-1-tablet$prefix.png") - - // Basics - composeTestRule.onNodeWithText("Basics").performClick() - composeTestRule.takeScreenshot("screen-2-tablet$prefix.png") - } - - @Test - fun takePhoneLightAndDarkScreenshots() { - takePhoneScreenshots("") - scenario = ActivityScenario.launch(MainActivity::class.java) - scenario.onActivity { - setDefaultNightMode(MODE_NIGHT_YES) - } - takePhoneScreenshots("-dark") - } - - private fun takePhoneScreenshots(prefix: String) { - // Command list - composeTestRule.onNodeWithText("Commands").performClick() - - composeTestRule.onNodeWithContentDescription("Search").performClick() - composeTestRule.onNodeWithContentDescription("SearchField") - .performTextInput("mk") - - composeTestRule.takeScreenshot("screen-4$prefix.png") - - // Command detail - composeTestRule.onNodeWithText("mkdir").performClick() - composeTestRule.onNodeWithText("SYNOPSIS").performClick() - composeTestRule.onNodeWithText("DESCRIPTION").performClick() - composeTestRule.takeScreenshot("screen-1$prefix.png") - - // Basics - composeTestRule.onNodeWithText("Basics").performClick() - composeTestRule.onNodeWithText("System information").performClick() - composeTestRule.takeScreenshot("screen-3$prefix.png") - - // Tips - composeTestRule.onNodeWithText("Tips").performClick() - composeTestRule.onNodeWithContentDescription("Scroll") - .performScrollToNode(hasText("$ [command] --help")) - composeTestRule.takeScreenshot("screen-2$prefix.png") - } - - private fun ComposeTestRule.takeScreenshot(file: String) { - // TODO: Find better way to wait for animations to finish - runBlocking { - delay(1000L) - } - onRoot() - .captureToImage() - .asAndroidBitmap() - .save(file) - } - - private fun Bitmap.save(file: String) { - val path = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.canonicalPath - FileOutputStream("$path/$file").use { out -> - compress(Bitmap.CompressFormat.PNG, 100, out) - } - } -} diff --git a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeTests.kt b/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeTests.kt deleted file mode 100644 index 698fb21..0000000 --- a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeTests.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.content.Context -import androidx.compose.ui.test.* -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.preference.PreferenceManager -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.linuxcommandlibrary.shared.copyDatabase -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.initDatabase -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.koin.java.KoinJavaComponent.inject - -/** - * Test for navigation, search and booksmarks. More tests to come - */ -@RunWith(AndroidJUnit4::class) -class ComposeTests { - - @get:Rule - val composeTestRule = createComposeRule() - - @Before - fun setUp() { - val context: Context = ApplicationProvider.getApplicationContext() - copyDatabase(context) - initDatabase(context) - - val dataManager: com.inspiredandroid.linuxcommandbibliotheca.DataManager by inject(com.inspiredandroid.linuxcommandbibliotheca.DataManager::class.java) - dataManager.updateDatabaseVersion() - - // Clear bookmarks - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.edit().putString("KEY_BOOKMARKS", "").apply() - - composeTestRule.setContent { LinuxApp() } - } - - /** - * Click though BottomNavigationBar and assert that TopAppBar titles are correct - */ - @Test - fun testBottomNavigation() { - composeTestRule.onNodeWithText("Tips").performClick() - composeTestRule.onNodeWithContentDescription("TopAppBarTitle").assertTextEquals("Tips") - composeTestRule.onNodeWithText("Commands").performClick() - composeTestRule.onNodeWithContentDescription("Back").performClick() - composeTestRule.onNodeWithContentDescription("TopAppBarTitle").assertTextEquals("Commands") - composeTestRule.onNodeWithText("Basics").performClick() - composeTestRule.onNodeWithContentDescription("TopAppBarTitle").assertTextEquals("Basics") - } - - /** - * Test if info is shown when search for a command that doesn't exist and if command description - * is shown when search for an existing command - */ - @Test - fun testSearch() { - composeTestRule.onNodeWithText("Commands").performClick() - - composeTestRule.onNodeWithContentDescription("Search").performClick() - - // Search for a command that doesn't exist - composeTestRule.onNodeWithContentDescription("SearchField") - .performTextInput("CommandThatDoesn'tExist") - composeTestRule.onNodeWithText("404 command not found").assertIsDisplayed() - composeTestRule.onNodeWithContentDescription("SearchField").performTextClearance() - - // Search for an existing command - val firstCommand = databaseHelper.getCommands().last() - composeTestRule.onNodeWithContentDescription("SearchField") - .performTextInput(firstCommand.name) - composeTestRule.onNodeWithText(firstCommand.description).assertIsDisplayed() - } - - /** - * Test if bookmarks in command detail and command list are shown correctly - */ - @Test - fun testBookmarks() { - val firstCommand = databaseHelper.getCommands().first() - - composeTestRule.onNodeWithText("Commands").performClick() - - composeTestRule.onNodeWithContentDescription("Search").performClick() - - // Search for first command and go to command detail screen - composeTestRule.onNodeWithContentDescription("SearchField") - .performTextInput(firstCommand.name) - composeTestRule.onNodeWithText(firstCommand.description).performClick() - - // Click bookmark icon and check if icon/contentDescription changed - composeTestRule.onNodeWithContentDescription("Add bookmark").performClick() - composeTestRule.mainClock.advanceTimeBy(1000) - composeTestRule.waitForIdle() - composeTestRule.onNodeWithContentDescription("Remove bookmark").assertIsDisplayed() - - // Go back to search/list and check if bookmark icon is visible - composeTestRule.onNodeWithContentDescription("Back").performClick() - composeTestRule.onNodeWithContentDescription("Bookmarked").assertIsDisplayed() - } - - @Test - fun testBasicsScreen() { - val firstBasicCategory = databaseHelper.getBasics().first() - - // Click on first category - composeTestRule.onNodeWithText(firstBasicCategory.title).performClick() - composeTestRule.onNodeWithContentDescription("TopAppBarTitle") - .assertTextEquals(firstBasicCategory.title) - - val basicGroup = databaseHelper.getBasicGroupsByQuery(firstBasicCategory.id).first() - - // Click on first group - composeTestRule.onNodeWithText(basicGroup.description).performClick() - - // Check if commands of group expanded and therefore share icon(s) are visible - composeTestRule.onAllNodesWithContentDescription("Share").assertAny(isEnabled()) - } -} diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml deleted file mode 100644 index 8c69ed8..0000000 --- a/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/DataManager.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/DataManager.kt deleted file mode 100644 index 5d84a63..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/DataManager.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.content.Context -import android.content.SharedPreferences -import androidx.core.content.edit -import androidx.preference.PreferenceManager - -class DataManager(context: Context) { - - val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - - private val bookmarksIds = getBookmarkIds() - - private fun getBookmarkIds(): MutableList { - val bookmarksChain = prefs.getString(KEY_BOOKMARKS, "") ?: "" - return bookmarksChain.split(",").mapNotNull { it.trim().toLongOrNull() }.toMutableList() - } - - private fun saveBookmarkIds() { - val bookmarksChain = bookmarksIds.joinToString(separator = ",") - prefs.edit { putString(KEY_BOOKMARKS, bookmarksChain) } - } - - fun addBookmark(id: Long) { - bookmarksIds.add(id) - saveBookmarkIds() - } - - fun removeBookmark(id: Long) { - bookmarksIds.remove(id) - saveBookmarkIds() - } - - fun hasBookmark(id: Long): Boolean = bookmarksIds.contains(id) - - fun isDatabaseUpToDate(): Boolean { - val databaseVersion = prefs.getInt(KEY_DATABASE_VERSION, 0) - return databaseVersion == CURRENT_DATABASE_VERSION - } - - fun updateDatabaseVersion() { - prefs.edit { putInt(KEY_DATABASE_VERSION, CURRENT_DATABASE_VERSION) } - } - - fun setAutoExpandSections(autoExpand: Boolean) { - prefs.edit { putBoolean(KEY_AUTO_EXPAND_SECTIONS, autoExpand) } - } - - fun isAutoExpandSections(): Boolean = prefs.getBoolean(KEY_AUTO_EXPAND_SECTIONS, false) - - companion object { - const val KEY_BOOKMARKS = "KEY_BOOKMARKS" - const val KEY_DATABASE_VERSION = "DATABASE_VERSION" - const val KEY_AUTO_EXPAND_SECTIONS = "auto_expand_sections" - const val CURRENT_DATABASE_VERSION = 32 - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ExtensionFunctions.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ExtensionFunctions.kt deleted file mode 100644 index cef38a4..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ExtensionFunctions.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import androidx.navigation.NavBackStackEntry -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.getHtmlFileName - -fun NavBackStackEntry.getCommandId(): Long? { - var commandId = - arguments?.getString("commandId")?.toLongOrNull() - if (commandId == null) { - // get id by command name when opened via deeplink - val commandName = arguments?.getString("commandName") ?: "" - val command = databaseHelper.getCommand(commandName) - commandId = command?.id - } - return commandId -} - -fun NavBackStackEntry.getCategoryId(): Long? { - var categoryId = - arguments?.getString("categoryId")?.toLongOrNull() - if (categoryId == null) { - // get id by category name when opened via deeplink - val categoryName = - arguments?.getString("deepLinkCategoryName") ?: "" - val categories = databaseHelper.getBasics() - categoryId = categories.firstOrNull { it.getHtmlFileName() == categoryName }?.id - } - return categoryId -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/IconResources.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/IconResources.kt deleted file mode 100644 index 32de97d..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/IconResources.kt +++ /dev/null @@ -1,264 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import databases.BasicCategory -import databases.BasicGroup - -fun BasicGroup.getIconResource(): Int = when (id.toInt()) { - 1205 -> R.drawable.ic_info_40dp - 1206 -> R.drawable.ic_icons8_restore_window - 1207 -> R.drawable.ic_icons8_compass - 1208 -> R.drawable.ic_icons8_console - 1212 -> R.drawable.ic_icons8_hand_with_pen - 1209 -> R.drawable.ic_info_40dp - 1211 -> R.drawable.ic_icons8_keyboard - 1210 -> R.drawable.ic_icons8_compass - 1214 -> R.drawable.ic_info_40dp - 1215 -> R.drawable.ic_icons8_keyboard - 1213 -> R.drawable.ic_icons8_compass - 1199 -> R.drawable.ic_file - 1200 -> R.drawable.ic_icons8_compass - 1201 -> R.drawable.ic_icons8_copy - 1202 -> R.drawable.ic_icons8_hand_with_pen - 1203 -> R.drawable.ic_search_40dp - 1204 -> R.drawable.ic_icons8_restore_window - 1165 -> R.drawable.ic_icons8_copy - 1166 -> R.drawable.ic_icons8_new - 1167 -> R.drawable.ic_icons8_save - 1168 -> R.drawable.ic_info_40dp - 1169 -> R.drawable.ic_icons8_plus - 1170 -> R.drawable.ic_icons8_numbered_list - 1171 -> R.drawable.ic_delete_black_24dp - 1172 -> R.drawable.ic_icons8_merge - 1173 -> R.drawable.ic_icons8_arrow - 1174 -> R.drawable.ic_icons8_arrow - 1175 -> R.drawable.ic_icons8_plus - 1176 -> R.drawable.ic_delete_black_24dp - 1177 -> R.drawable.ic_icons8_save - 1178 -> R.drawable.ic_available_updates - 1179 -> R.drawable.ic_icons8_plus - 1180 -> R.drawable.ic_icons8_user_male_circle - 1181 -> R.drawable.ic_icons8_user_male_circle - 1182 -> R.drawable.ic_info_40dp - 1183 -> R.drawable.ic_icons8_undo - 1184 -> R.drawable.ic_icons8_hide - 1185 -> R.drawable.ic_icons8_save - 1186 -> R.drawable.ic_icons8_save - 1187 -> R.drawable.ic_icons8_visible - 1188 -> R.drawable.ic_delete_black_24dp - 1189 -> R.drawable.ic_delete_black_24dp - 1190 -> R.drawable.ic_delete_black_24dp - 1191 -> R.drawable.ic_delete_black_24dp - 1192 -> R.drawable.ic_delete_black_24dp - 125 -> R.drawable.ic_file_download_black_24dp - 126 -> R.drawable.ic_file - 127 -> R.drawable.ic_delete_black_24dp - 128 -> R.drawable.ic_search_40dp - 129 -> R.drawable.ic_info_40dp - 130 -> R.drawable.ic_icons8_synchronize - 131 -> R.drawable.ic_arrow_upward_black_24dp - 132 -> R.drawable.ic_add_rule - 7, 5, 1236, 1248, 1235, 1247, 1246, 1238, 8, 6, 10, 1241, 1245, 9, 1244, 1243, 1, 4, 1242, 2, 3, 0, 1237, 2630 -> R.drawable.ic_icon_controller - 1163 -> R.drawable.ic_vpn_key_black_24dp - 1164, 1162, 1161, 1160, 1159 -> R.drawable.ic_icons8_connected - 1231, 1232 -> R.drawable.ic_file - 1158 -> R.drawable.ic_icons8_circled_pause - 26, 91, 92, 93, 94, 95 -> R.drawable.ic_search_40dp - 96, 97, 98, 100, 2399, 99 -> R.drawable.ic_file - 41 -> R.drawable.ic_electronics - 42 -> R.drawable.ic_battery_90_black_24dp - 43, 44 -> R.drawable.ic_bluetooth_black_24dp - 45 -> R.drawable.ic_icons8_network_card - 46 -> R.drawable.ic_memory_slot - 56, 65 -> R.drawable.ic_icons8_tv_off - 57 -> R.drawable.ic_icons8_tv_on - 47 -> R.drawable.ic_icons8_linux - 48 -> R.drawable.ic_icons8_root_server - 49 -> R.drawable.ic_usb_black_48dp - 50 -> R.drawable.ic_icons8_flow_chart - 51 -> R.drawable.ic_ip_address - 58 -> R.drawable.ic_refresh_black_24dp - 59 -> R.drawable.ic_power_settings_new_black_24dp - 55 -> R.drawable.ic_icons8_calendar_1 - 52, 60 -> R.drawable.ic_timer_black_24dp - 53 -> R.drawable.ic_icons8_hdd - 61 -> R.drawable.ic_stop_bluetooth - 62 -> R.drawable.ic_bluetooth_start - 63 -> R.drawable.ic_stop_wifi - 64 -> R.drawable.ic_wifi_start - 28 -> R.drawable.ic_icons8_work - 29 -> R.drawable.ic_icons8_undo - 31 -> R.drawable.ic_icons8_kitchen_scales - 81 -> R.drawable.ic_file_download_white - 191 -> R.drawable.ic_vip_lookup_white_48dp - 83 -> R.drawable.ic_icons8_ping_pong - 189 -> R.drawable.ic_settings_black_24dp - 27, 32 -> R.drawable.ic_icons8_show_property - 16 -> R.drawable.ic_file_move_white - 15 -> R.drawable.ic_file_copy_white_48dp - 20 -> R.drawable.ic_change_folder_white - 13 -> R.drawable.ic_file_content_white - 19 -> R.drawable.ic_folder_list_white - 18 -> R.drawable.ic_delete_folder_white_48dp - 17 -> R.drawable.ic_create_new_folder_white - 12 -> R.drawable.ic_delete_file_white - 11 -> R.drawable.ic_create_file_white - 14 -> R.drawable.ic_file_edit_white_48dp - 21 -> R.drawable.ic_icons8_home - 22 -> R.drawable.ic_icons8_mother - 23 -> R.drawable.ic_icons8_downloads_folder - 25 -> R.drawable.ic_file_link_white_48dp - 30 -> R.drawable.ic_icons8_exe - 102 -> R.drawable.ic_remove_user_group - 101 -> R.drawable.ic_icons8_add_user_group - 103 -> R.drawable.ic_edit_group - 107 -> R.drawable.ic_user_password - 104 -> R.drawable.ic_icons8_add_user - 105 -> R.drawable.ic_icons8_remove_user_male - 108 -> R.drawable.ic_icons8_moderator_male - 106 -> R.drawable.ic_icons8_edit_user - 110 -> R.drawable.ic_add_user_to_group_white_48dp - 111 -> R.drawable.ic_add_user_to_group_white_48dp - 112 -> R.drawable.ic_remove_user_from_group_white_48dp - 114 -> R.drawable.ic_list_user_white_48dp - 33 -> R.drawable.ic_icons8_reuse - 34 -> R.drawable.ic_icons8_delete_trash - 35 -> R.drawable.ic_icons8_add_trash - 36 -> R.drawable.ic_icons8_file_preview - 37 -> R.drawable.ic_file_permission_white_48dp - 38 -> R.drawable.ic_icons8_user_folder - 24 -> R.drawable.ic_folder_path_white - 39 -> R.drawable.ic_icons8_user_male_circle - 40 -> R.drawable.ic_icons8_group_foreground_selected - 113 -> R.drawable.ic_list_groups_white_48dp - 109 -> R.drawable.ic_info_40dp - 1193 -> R.drawable.ic_icons8_print_file - 1194 -> R.drawable.ic_icons8_visible - 1195 -> R.drawable.ic_icons8_cancel - 1196 -> R.drawable.ic_info_40dp - 1197 -> R.drawable.ic_icons8_circled_play - 1198 -> R.drawable.ic_icons8_circled_pause - 1230 -> R.drawable.ic_icons8_plus - 1229 -> R.drawable.ic_delete_black_24dp - 1228 -> R.drawable.ic_icons8_plus - 1227 -> R.drawable.ic_icons8_plus - 1226 -> R.drawable.ic_icons8_print - 80 -> R.drawable.ic_list_interfaces_white_48dp - 82 -> R.drawable.ic_vip_lookup_white_48dp - 84 -> R.drawable.ic_settings_black_24dp - 85 -> R.drawable.ic_icons8_visible - 86 -> R.drawable.ic_fingerprint_black_24dp - 88 -> R.drawable.ic_dns_black_24dp - 89 -> R.drawable.ic_ip_address - 90 -> R.drawable.ic_list_sockets_white_48dp - 143 -> R.drawable.ic_search_executeable_man_white_48dp - 87 -> R.drawable.ic_search_source_man_white_48dp - 320 -> R.drawable.ic_icons8_show_property - 138 -> R.drawable.ic_fingerprint_black_24dp - 116 -> R.drawable.ic_vpn_key_black_24dp - 221 -> R.drawable.ic_list_sockets_white_48dp - 137 -> R.drawable.ic_file_edit_white_48dp - 71 -> R.drawable.ic_volume_off_black_24dp - 67 -> R.drawable.ic_webcam_white_48dp - 66 -> R.drawable.ic_desktop_windows_black_24dp - 68 -> R.drawable.ic_icons8_talk - 69, 70 -> R.drawable.ic_volume_up_black_24dp - 72, 73, 75, 76, 77, 78, 1239, 1240, 74 -> R.drawable.ic_switch_video_white_48dp - 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440 -> R.drawable.ic_videogame_asset_black_24dp - 443 -> R.drawable.ic_vpn_key_black_24dp - 450 -> R.drawable.ic_wifi_black_24dp - 446, 1495, 457, 1516, 1509 -> R.drawable.ic_info_black_24dp - 451 -> R.drawable.ic_report_black_24dp - 445 -> R.drawable.ic_loupe_black_24dp - 444 -> R.drawable.ic_fingerprint_black_24dp - 448 -> R.drawable.ic_public_black_24dp - 447 -> R.drawable.ic_healing_black_24dp - 441 -> R.drawable.ic_flash_on_black_24dp - 449 -> R.drawable.ic_storage_black_24dp - 454 -> R.drawable.ic_file - 456 -> R.drawable.ic_search_black_24dp - 460 -> R.drawable.ic_add_rule - 453 -> R.drawable.ic_file_download_black_24dp - 458 -> R.drawable.ic_available_updates - 459 -> R.drawable.ic_arrow_upward_black_24dp - 455, 1519, 1511, 1528, 1529, 1530, 1531, 1532 -> R.drawable.ic_delete_black_24dp - 461 -> R.drawable.ic_icons8_show_property - 1492 -> R.drawable.ic_icons8_home - 1493 -> R.drawable.ic_icons8_mother - 1496 -> R.drawable.ic_icons8_work - 1497 -> R.drawable.ic_icons8_undo - 1494 -> R.drawable.ic_icons8_calendar_1 - 1503 -> R.drawable.ic_vpn_key_black_24dp - 1487, 1488, 1489, 1490, 1491 -> R.drawable.ic_file - 1498 -> R.drawable.ic_icons8_cancel - 1499, 1500, 1501, 1502, 1504 -> R.drawable.ic_icons8_connected - 1505 -> R.drawable.ic_icons8_copy - 1515 -> R.drawable.ic_icons8_new - 1506, 1525, 1512 -> R.drawable.ic_icons8_save - 1517, 1510, 1523 -> R.drawable.ic_icons8_plus - 1518 -> R.drawable.ic_icons8_numbered_list - 1522 -> R.drawable.ic_icons8_merge - 1520, 1521 -> R.drawable.ic_icons8_arrow - 1524 -> R.drawable.ic_icons8_synchronize - 1507, 1508 -> R.drawable.ic_icons8_user_male_circle - 1513 -> R.drawable.ic_icons8_redo - 1514 -> R.drawable.ic_icons8_hide - 1527 -> R.drawable.ic_icons8_visible - 178 -> R.drawable.ic_icons8_show_property - 154 -> R.drawable.ic_icons8_exe - 141 -> R.drawable.ic_icons8_reuse - 144 -> R.drawable.ic_icons8_delete_trash - 411 -> R.drawable.ic_icons8_add_trash - 247 -> R.drawable.ic_icons8_visible - 169 -> R.drawable.ic_icons8_cancel - 3731 -> R.drawable.ic_info_black_24dp - 186 -> R.drawable.ic_icons8_circled_play - 187 -> R.drawable.ic_icons8_circled_pause - 226 -> R.drawable.ic_icons8_file_preview - 231 -> R.drawable.ic_file_permission_white_48dp - 1217 -> R.drawable.ic_icons8_coin_wallet - 1218 -> R.drawable.ic_icons8_bot - 1216 -> R.drawable.ic_icons8_golden_fever - 1233 -> R.drawable.ic_icons8_teacher_hiring - 117 -> R.drawable.ic_fingerprint_black_24dp - 115 -> R.drawable.ic_flash_on_black_24dp - 118 -> R.drawable.ic_loupe_black_24dp - 119 -> R.drawable.ic_info_40dp - 120 -> R.drawable.ic_healing_black_24dp - 121 -> R.drawable.ic_public_black_24dp - 122 -> R.drawable.ic_storage_black_24dp - 123 -> R.drawable.ic_wifi_black_24dp - 124 -> R.drawable.ic_report_black_24dp - 79 -> R.drawable.ic_vpn_key_black_24dp - 54 -> R.drawable.ic_icons8_document - 1222, 1221, 1223 -> R.drawable.ic_icon_mouse - 1220 -> R.drawable.ic_icons8_clipboard - 1219 -> R.drawable.ic_icons8_treatment - 1225, 1224 -> R.drawable.ic_icons8_keyboard - 2403 -> R.drawable.ic_icon_mouse - 2400 -> R.drawable.ic_icons8_keyboard - 2402 -> R.drawable.ic_file - else -> R.drawable.ic_icons8_console -} - -fun BasicCategory.getIconResource(): Int = when (title) { - "One-liners" -> R.drawable.ic_icons8_hand_with_pen - "System information" -> R.drawable.ic_icon_system_task - "System control" -> R.drawable.ic_settings_black_40dp - "Users & Groups" -> R.drawable.ic_icon_user - "Files & Folders" -> R.drawable.ic_file - "Printing" -> R.drawable.ic_icons8_print - "Network" -> R.drawable.ic_network_card_40dp - "Search & Find" -> R.drawable.ic_search_40dp - "GIT" -> R.drawable.ic_icon_git - "SSH" -> R.drawable.ic_icons8_console - "Video & Audio" -> R.drawable.ic_video_trimming_40dp - "Package manager" -> R.drawable.ic_package_40 - "Hacking tools" -> R.drawable.ic_icon_skull - "Terminal games" -> R.drawable.ic_icon_controller - "VIM Texteditor", "Emacs Texteditor", "Nano Texteditor", "Pico Texteditor", "Micro Texteditor" -> R.drawable.ic_icons8_text - "Crypto currencies" -> R.drawable.ic_icon_bitcoin - "Input" -> R.drawable.ic_icon_mouse - "JSON" -> R.drawable.ic_icon_json - "Fun" -> R.drawable.ic_icon_fun - else -> R.drawable.ic_icon_mouse -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/InitializeDatabaseActivity.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/InitializeDatabaseActivity.kt deleted file mode 100644 index 4d06450..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/InitializeDatabaseActivity.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.content.Intent -import android.os.Bundle -import androidx.activity.compose.setContent -import androidx.appcompat.app.AppCompatActivity -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.InitializeDatabaseScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -class InitializeDatabaseActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setContent { - LinuxTheme { - InitializeDatabaseScreen { - startActivity(Intent(this, MainActivity::class.java)) - finish() - } - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/LinuxApplication.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/LinuxApplication.kt deleted file mode 100644 index 80bf1e5..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/LinuxApplication.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.app.Application -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basiccategories.BasicCategoriesViewModel -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups.BasicGroupsViewModel -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commanddetail.CommandDetailViewModel -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commandlist.CommandListViewModel -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.search.SearchViewModel -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.tips.TipsViewModel -import org.koin.android.ext.koin.androidContext -import org.koin.android.ext.koin.androidLogger -import org.koin.core.context.GlobalContext.startKoin -import org.koin.core.module.dsl.* -import org.koin.dsl.module - -class LinuxApplication : Application() { - - override fun onCreate() { - super.onCreate() - - startKoin { - androidLogger() - androidContext(this@LinuxApplication) - modules(appModule) - } - } - - private val appModule = module { - single { DataManager(androidContext()) } - - viewModel { BasicGroupsViewModel(get()) } - viewModel { BasicCategoriesViewModel() } - viewModel { (commandId: Long) -> CommandDetailViewModel(commandId, get()) } - viewModel { TipsViewModel() } - viewModel { CommandListViewModel(get()) } - viewModel { SearchViewModel() } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/MainActivity.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/MainActivity.kt deleted file mode 100644 index eda3208..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/MainActivity.kt +++ /dev/null @@ -1,251 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca - -import android.content.Intent -import android.os.Bundle -import androidx.activity.SystemBarStyle -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextRange -import androidx.compose.ui.text.input.TextFieldValue -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import androidx.navigation.navArgument -import androidx.navigation.navDeepLink -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.BottomBar -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.TopBar -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basiccategories.BasicCategoriesScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups.BasicGroupsScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commanddetail.CommandDetailScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commandlist.CommandListScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.search.SearchScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.tips.TipsScreen -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LocalCustomColors -import com.linuxcommandlibrary.shared.hasDatabase -import com.linuxcommandlibrary.shared.initDatabase -import org.koin.android.ext.android.inject - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -class MainActivity : AppCompatActivity() { - - private val dataManager by inject() - - override fun onCreate(savedInstanceState: Bundle?) { - enableEdgeToEdge(statusBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT), navigationBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT)) - super.onCreate(savedInstanceState) - - if (!hasDatabase(this) || !dataManager.isDatabaseUpToDate()) { - startActivity(Intent(this, InitializeDatabaseActivity::class.java)) - dataManager.updateDatabaseVersion() - finish() - return - } - - initDatabase(this) - - setContent { - LinuxTheme { - Box( - Modifier - .background(MaterialTheme.colors.primary) - .statusBarsPadding(), - ) { - Box( - Modifier - .background(LocalCustomColors.current.navBarBackground) - .systemBarsPadding(), - ) { - LinuxApp() - } - } - } - } - } -} - -const val DEEPLINK_URI = "https://linuxcommandlibrary.com" - -@Composable -fun LinuxApp() { - val navController = rememberNavController() - val navBackStackEntry = navController.currentBackStackEntryAsState() - val searchTextValue = remember { - mutableStateOf( - TextFieldValue(text = "", selection = TextRange(0)), - ) - } - val showSearch = remember { mutableStateOf(false) } - val onNavigate: (String) -> Unit = remember(navController) { { route -> navController.navigate(route) } } - - Scaffold( - topBar = { - TopBar( - navBackStackEntry = navBackStackEntry, - textFieldValue = searchTextValue, - onNavigateBack = { - navController.popBackStack() - }, - isSearchVisible = showSearch, - ) - }, - bottomBar = { - BottomBar( - navController = navController, - resetSearch = { - searchTextValue.value = TextFieldValue(text = "", selection = TextRange(0)) - showSearch.value = false - }, - ) - }, - ) { innerPadding -> - Box( - modifier = Modifier.padding(innerPadding), - ) { - NavHost( - navController = navController, - startDestination = Screen.Basics.route, - ) { - composable( - Screen.Basics.route, - deepLinks = listOf( - navDeepLink { uriPattern = "$DEEPLINK_URI/basics" }, - navDeepLink { uriPattern = "$DEEPLINK_URI/basics.html" }, - ), - ) { - BasicCategoriesScreen( - onNavigate = onNavigate, - ) - } - composable( - Screen.Commands.route, - deepLinks = listOf( - navDeepLink { uriPattern = "$DEEPLINK_URI/" }, - navDeepLink { uriPattern = "$DEEPLINK_URI/index.html" }, - ), - ) { - CommandListScreen( - onNavigate = onNavigate, - ) - } - composable( - Screen.Tips.route, - deepLinks = listOf( - navDeepLink { uriPattern = "$DEEPLINK_URI/tips" }, - navDeepLink { uriPattern = "$DEEPLINK_URI/tips.html" }, - ), - ) { - TipsScreen(onNavigate) - } - composable( - "basicgroups?categoryId={categoryId}&categoryName={categoryName}", - arguments = listOf( - navArgument("categoryId") { defaultValue = "" }, - navArgument("categoryName") {}, - ), - deepLinks = listOf( - navDeepLink { - uriPattern = "$DEEPLINK_URI/basic/{categoryName}.html" - }, - navDeepLink { uriPattern = "$DEEPLINK_URI/basic/{categoryName}" }, - ), - ) { backStackEntry -> - val categoryId = backStackEntry.getCategoryId() - if (categoryId != null) { - BasicGroupsScreen( - categoryId = categoryId, - onNavigate = onNavigate, - ) - } else { - // open tips screen on invalid deeplink parameters - TipsScreen(onNavigate) - } - } - composable( - "command?commandId={commandId}&commandName={commandName}", - arguments = listOf( - navArgument("commandId") { defaultValue = "" }, - navArgument("commandName") {}, - ), - deepLinks = listOf( - navDeepLink { uriPattern = "$DEEPLINK_URI/man/{commandName}.html" }, - navDeepLink { uriPattern = "$DEEPLINK_URI/man/{commandName}" }, - ), - ) { backStackEntry -> - val commandId = backStackEntry.getCommandId() - if (commandId != null) { - CommandDetailScreen( - commandId = commandId, - onNavigate = onNavigate, - ) - } else { - // open tips screen on invalid deeplink parameters - TipsScreen(onNavigate) - } - } - } - - val isSearchVisible by remember(navBackStackEntry, searchTextValue) { - derivedStateOf { - searchTextValue.value.text.isNotEmpty() && - navBackStackEntry.value?.destination?.route?.startsWith("command?") == false - } - } - AnimatedVisibility( - visible = isSearchVisible, - enter = fadeIn(animationSpec = tween(300)), - exit = fadeOut(animationSpec = tween(durationMillis = 300, delayMillis = 300)), // work around for navigation overlaps - ) { - SearchScreen( - searchText = searchTextValue.value.text, - onNavigate = remember(navController) { { route -> navController.navigate(route) } }, - ) - } - } - } -} - -sealed class Screen( - val route: String, - @StringRes val resourceId: Int, - @DrawableRes val drawableRes: Int, -) { - data object Commands : Screen("commands", R.string.commands, R.drawable.ic_search_40dp) - data object Basics : Screen("basics", R.string.basics, R.drawable.ic_puzzle) - data object Tips : Screen("tips", R.string.tips, R.drawable.ic_idea) -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/BottomBar.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/BottomBar.kt deleted file mode 100644 index 479fcde..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/BottomBar.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import androidx.compose.foundation.layout.size -import androidx.compose.material.BottomNavigation -import androidx.compose.material.BottomNavigationItem -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.navigation.NavDestination.Companion.hierarchy -import androidx.navigation.NavGraph.Companion.findStartDestination -import androidx.navigation.NavHostController -import androidx.navigation.compose.currentBackStackEntryAsState -import com.inspiredandroid.linuxcommandbibliotheca.Screen -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LocalCustomColors - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -val bottomBarItems = listOf( - Screen.Basics, - Screen.Tips, - Screen.Commands, -) - -@Composable -fun BottomBar( - navController: NavHostController, - resetSearch: () -> Unit, -) { - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentDestination = navBackStackEntry?.destination - - BottomNavigation( - backgroundColor = LocalCustomColors.current.navBarBackground, - elevation = 0.dp, - ) { - bottomBarItems.forEach { screen -> - BottomNavigationItem( - icon = { - Icon( - painter = painterResource(id = screen.drawableRes), - contentDescription = null, - modifier = Modifier.size(24.dp), - ) - }, - label = { Text(stringResource(screen.resourceId)) }, - selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true, - selectedContentColor = MaterialTheme.colors.primary, - unselectedContentColor = MaterialTheme.colors.onSurface, - onClick = { - // Pop back stack if current route starts with "command?" - // This specific logic might need adjustment based on detailed navigation graph behavior - // For example, ensure it doesn't pop too much or interfere with expected navigation. - // A more robust way might be to navigate to a specific point before the command details. - while (navController.currentBackStackEntry?.destination?.route?.startsWith("command?") == true) { - navController.popBackStack() - } - navController.navigate(screen.route) { - popUpTo(navController.graph.findStartDestination().id) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - resetSearch() - }, - ) - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/CommandView.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/CommandView.kt deleted file mode 100644 index 7263f2e..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/CommandView.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import android.content.Context -import android.content.Intent -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Share -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.LinkAnnotation -import androidx.compose.ui.text.ParagraphStyle -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.R -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme -import com.linuxcommandlibrary.shared.CommandElement -import com.linuxcommandlibrary.shared.databaseHelper -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun CommandView( - command: String, - elements: ImmutableList, - onNavigate: (String) -> Unit = {}, - verticalPadding: Dp = 6.dp, -) { - val codeColor = MaterialTheme.colors.primary - val baseAnnotatedString = remember(elements, codeColor) { - buildAnnotatedString { - elements.forEach { element -> - when (element) { - is CommandElement.Text -> { - append(element.text) - } - is CommandElement.Man -> { - val start = this.length - withStyle(style = SpanStyle(color = codeColor)) { - append(element.man) - } - val end = this.length - addLink( - LinkAnnotation.Clickable( - tag = "man:${element.man}", - linkInteractionListener = { - val manCommand = databaseHelper.getCommand(element.man) - if (manCommand != null) { - onNavigate("command?commandId=${manCommand.id}&commandName=${manCommand.name}") - } - }, - ), - start, - end, - ) - } - is CommandElement.Url -> { - val start = this.length - withStyle(style = SpanStyle(color = codeColor)) { - append(element.command) - } - val end = this.length - addLink( - LinkAnnotation.Url(element.url), - start, - end, - ) - } - } - } - } - } - - val finalAnnotatedString = remember(baseAnnotatedString) { - if (baseAnnotatedString.text.isEmpty()) { - baseAnnotatedString - } else { - buildAnnotatedString { - append(baseAnnotatedString) - addStyle( - style = ParagraphStyle(), // Default ParagraphStyle - start = 0, - end = baseAnnotatedString.text.length, - ) - } - } - } - - Row(modifier = Modifier.padding(start = 12.dp, end = 4.dp).padding(vertical = verticalPadding)) { - Text( - text = finalAnnotatedString, - modifier = Modifier - .weight(1f) - .align(Alignment.CenterVertically), - style = MaterialTheme.typography.subtitle2, - ) - - val context = LocalContext.current - val shareAction = remember(context, command) { { shareCommand(context, command) } } - IconButton( - modifier = Modifier.align(Alignment.CenterVertically), - onClick = shareAction, - ) { - Icon( - imageVector = Icons.Filled.Share, - contentDescription = stringResource(R.string.share), - ) - } - } -} - -fun shareCommand(context: Context, command: String) { - val intent = Intent(Intent.ACTION_SEND) - intent.type = "text/plain" - intent.putExtra( - Intent.EXTRA_TEXT, - command.dropWhile { it == '$' || it.isWhitespace() }.replace("\\n", ""), - ) - try { - context.startActivity(Intent.createChooser(intent, "Share command")) - } catch (e: Exception) { - e.printStackTrace() - } -} - -@Composable -@Preview -fun CommandViewPreview() { - LinuxTheme { - CommandView( - command = "$ find ex?mple.txt", - elements = listOf( - CommandElement.Text("$ "), - CommandElement.Man("find"), - CommandElement.Text(" ex?mple.txt"), - ).toImmutableList(), - ) - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/HighlightedText.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/HighlightedText.kt deleted file mode 100644 index 8d3e7f0..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/HighlightedText.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.tooling.preview.Preview -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun HighlightedText( - text: String, - pattern: String, - maxLines: Int = 1, -) { - if (pattern.isEmpty()) { - Text( - text = text, - maxLines = maxLines, - overflow = TextOverflow.Ellipsis, - ) - } else if (text.equals(pattern, ignoreCase = true)) { - Text( - text = text, - maxLines = maxLines, - overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colors.primary, - ) - } else { - val highlightColor = MaterialTheme.colors.primary - val annotatedString = remember(text, pattern, highlightColor) { - buildAnnotatedString { - val splitText = text.split(pattern, ignoreCase = true) - splitText.forEachIndexed { index, s -> - append(s) - if (index != splitText.size - 1) { - withStyle(style = SpanStyle(color = highlightColor)) { - append(pattern) - } - } - } - } - } - Text( - text = annotatedString, - maxLines = maxLines, - overflow = TextOverflow.Ellipsis, - ) - } -} - -@Composable -@Preview -fun HighlightedTextPreview() { - LinuxTheme { - HighlightedText( - text = "pacman", - pattern = "cm", - ) - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/NestedCommandView.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/NestedCommandView.kt deleted file mode 100644 index 0cb1311..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/NestedCommandView.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.width -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme -import com.linuxcommandlibrary.shared.CommandElement -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun NestedCommandView( - text: String, - command: String, - commandElements: ImmutableList, - onNavigate: (String) -> Unit, -) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text, - fontWeight = FontWeight.Bold, - modifier = Modifier.width(40.dp), - textAlign = TextAlign.Center, - ) - CommandView( - command = command, - elements = commandElements, - onNavigate = onNavigate, - ) - } -} - -@Composable -@Preview -fun NestedCommandViewPreview() { - LinuxTheme { - NestedCommandView( - text = "", - command = "$ find ex?mple.txt", - commandElements = listOf( - CommandElement.Text("$ "), - CommandElement.Man("find"), - CommandElement.Text(" ex?mple.txt"), - ).toImmutableList(), - onNavigate = {}, - ) - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/NestedText.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/NestedText.kt deleted file mode 100644 index 63b7481..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/NestedText.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun NestedText(textLeft: String, textRight: String) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - textLeft, - fontWeight = FontWeight.Bold, - modifier = Modifier.width(40.dp), - textAlign = TextAlign.Center, - ) - Text(textRight, modifier = Modifier.padding(8.dp)) - } -} - -@Composable -@Preview -fun NestedTextPreview() { - LinuxTheme { - NestedText(textLeft = ">>", textRight = "redirect") - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/SectionTitle.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/SectionTitle.kt deleted file mode 100644 index 8457e9e..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/SectionTitle.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import androidx.compose.foundation.layout.padding -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun SectionTitle(modifier: Modifier = Modifier, title: String) { - Text( - text = title, - fontSize = 20.sp, - style = MaterialTheme.typography.subtitle1, - fontWeight = FontWeight.Bold, - modifier = modifier.padding(bottom = 4.dp), - ) -} - -@Preview -@Composable -fun SectionTitlePreview() { - LinuxTheme { - SectionTitle(title = "List of recent commands") - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/TopBar.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/TopBar.kt deleted file mode 100644 index 9e9eb51..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/composables/TopBar.kt +++ /dev/null @@ -1,360 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.composables - -import androidx.activity.compose.LocalActivity -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.LocalContentColor -import androidx.compose.material.MaterialTheme -import androidx.compose.material.OutlinedTextField -import androidx.compose.material.Text -import androidx.compose.material.TextFieldDefaults -import androidx.compose.material.TopAppBar -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.Search -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.key -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.lifecycle.ViewModelStoreOwner -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavBackStackEntry -import com.inspiredandroid.linuxcommandbibliotheca.R -import com.inspiredandroid.linuxcommandbibliotheca.getCommandId -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.AppInfoDialog -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.BookmarkFeedbackDialog -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commanddetail.CommandDetailViewModel -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.getHtmlFileName -import org.koin.androidx.compose.koinViewModel -import org.koin.core.parameter.parametersOf - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun TopBar( - navBackStackEntry: State, - textFieldValue: MutableState, - onNavigateBack: () -> Unit, - isSearchVisible: MutableState, -) { - val route = navBackStackEntry.value?.destination?.route - - if (route == "commands" || route == "basics") { - val hideSearchCallback = remember { { isSearchVisible.value = false } } - val showSearchCallback = remember { { isSearchVisible.value = true } } - SearchTopBar( - title = getTitleByRoute(navBackStackEntry.value), - textFieldValue = textFieldValue, - isSearchVisible = isSearchVisible.value, - hideSearch = hideSearchCallback, - showSearch = showSearchCallback, - ) - } else if (route?.startsWith("command?") == true) { - DetailTopBar( - commandId = navBackStackEntry.value?.getCommandId() ?: 0, - title = getTitleByRoute(navBackStackEntry.value), - onNavigateBack = onNavigateBack, - ) - } else { - val title = getTitleByRoute(navBackStackEntry.value) - val showBackIcon = route != "tips" // Simplified from original when - val showAppInfoIcon = route == "tips" - GenericTopBar( - title = title, - showBackIcon = showBackIcon, - onNavigateBack = onNavigateBack, - showAppInfoIcon = showAppInfoIcon, - ) - } -} - -@Composable -private fun GenericTopBar( - title: String, - showBackIcon: Boolean, - onNavigateBack: () -> Unit, - showAppInfoIcon: Boolean, -) { - var showDialog by remember { mutableStateOf(false) } - - TopAppBar( - title = { - Text( - title, - modifier = Modifier.semantics { contentDescription = "TopAppBarTitle" }, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - }, - backgroundColor = MaterialTheme.colors.primary, - contentColor = Color.White, - navigationIcon = if (showBackIcon) { - { - IconButton(onClick = { onNavigateBack() }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.back), - ) - } - } - } else { - null - }, - actions = { - if (showAppInfoIcon) { - IconButton(onClick = { - showDialog = true - }) { - Icon( - imageVector = Icons.Filled.Info, - contentDescription = stringResource(R.string.info), - ) - } - } - }, - ) - if (showDialog) { - AppInfoDialog(onDismiss = { showDialog = false }) - } -} - -@Composable -private fun DetailTopBar( - commandId: Long, - viewModel: CommandDetailViewModel = koinViewModel( - key = commandId.toString(), - viewModelStoreOwner = LocalActivity.current as ViewModelStoreOwner, - parameters = { parametersOf(commandId) }, - ), - title: String, - onNavigateBack: () -> Unit, -) { - val uiState by viewModel.state.collectAsStateWithLifecycle() - val isAllExpanded by remember { derivedStateOf { uiState.isAllExpanded() } } - - TopAppBar( - title = { - Text( - title, - modifier = Modifier.semantics { contentDescription = "TopAppBarTitle" }, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - }, - backgroundColor = MaterialTheme.colors.primary, - contentColor = Color.White, - navigationIcon = { - IconButton(onClick = onNavigateBack) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.back), - ) - } - }, - actions = { - IconButton(onClick = { - viewModel.onToggleAllExpanded() - }) { - if (isAllExpanded) { - Icon( - painterResource(R.drawable.ic_collapse_all), - contentDescription = stringResource(R.string.collapse_all), - ) - } else { - Icon( - painterResource(R.drawable.ic_expand_all), - contentDescription = stringResource(R.string.expand_all), - ) - } - } - IconButton(onClick = { - if (uiState.isBookmarked) { - viewModel.removeBookmark() - } else { - viewModel.addBookmark() - } - }) { - if (uiState.isBookmarked) { - Icon( - painterResource(R.drawable.ic_bookmark_black_24dp), - contentDescription = stringResource(R.string.remove_bookmark), - ) - } else { - Icon( - painterResource(R.drawable.ic_bookmark_border_black_24dp), - contentDescription = stringResource(R.string.add_bookmark), - ) - } - } - }, - ) - - if (uiState.showBookmarkDialog) { - BookmarkFeedbackDialog(onDismiss = { viewModel.hideBookmarkDialog() }) - } -} - -@Composable -private fun SearchTopBar( - title: String, - textFieldValue: MutableState, - isSearchVisible: Boolean, - hideSearch: () -> Unit, - showSearch: () -> Unit, -) { - val focusRequester = remember { FocusRequester() } - - Row( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colors.primary) - .heightIn(min = 56.dp) - .padding(horizontal = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - if (isSearchVisible) { - IconButton(onClick = { - hideSearch() - textFieldValue.value = TextFieldValue("") - }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.back), - tint = Color.White, - ) - } - } - if (isSearchVisible) { - OutlinedTextField( - value = textFieldValue.value, - onValueChange = { textFieldValue.value = it }, - modifier = Modifier - .weight(1f) - .focusRequester(focusRequester) - .semantics { contentDescription = "SearchField" } - .padding(start = 8.dp, end = 8.dp), - placeholder = { Text("Search...", color = Color.White.copy(alpha = 0.7f)) }, - textStyle = MaterialTheme.typography.subtitle1.copy(color = Color.White), - colors = TextFieldDefaults.outlinedTextFieldColors( - textColor = Color.White, - cursorColor = Color.White, - focusedBorderColor = Color.Transparent, - unfocusedBorderColor = Color.Transparent, - disabledBorderColor = Color.Transparent, - backgroundColor = Color.Transparent, - trailingIconColor = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), - placeholderColor = LocalContentColor.current.copy(alpha = 0.7f), - ), - maxLines = 1, - singleLine = true, - ) - if (textFieldValue.value.text.isNotEmpty()) { - IconButton(onClick = { - textFieldValue.value = TextFieldValue("") - }) { - Icon( - imageVector = Icons.Filled.Close, - contentDescription = stringResource(id = R.string.reset), - tint = Color.White, - ) - } - } - } else { - Text( - title, - modifier = Modifier - .weight(1f) - .padding(start = 16.dp) // Standard title padding when no nav icon - .semantics { contentDescription = "TopAppBarTitle" }, - style = MaterialTheme.typography.h6.copy(color = LocalContentColor.current), // Use h6 for title as per Material - maxLines = 1, - overflow = TextOverflow.Ellipsis, - color = Color.White, - ) - } - - if (!isSearchVisible) { - IconButton(onClick = { - showSearch() - }) { - Icon( - imageVector = Icons.Filled.Search, - contentDescription = stringResource(R.string.search), - tint = Color.White, - ) - } - } - } - - LaunchedEffect(isSearchVisible) { - if (isSearchVisible) { - focusRequester.requestFocus() - } - } -} - -@Composable -private fun getTitleByRoute(backStackEntry: NavBackStackEntry?): String { - if (backStackEntry == null) { - return "Linux" - } - return when (val route = backStackEntry.destination.route) { - "commands" -> stringResource(R.string.commands) - "basics" -> stringResource(R.string.basics) - "tips" -> stringResource(R.string.tips) - else -> { - if (route?.startsWith("command?") == true) { - backStackEntry.arguments?.getString("commandName") ?: "" - } else if (route?.startsWith("basicgroups?") == true) { - val deeplinkName = backStackEntry.arguments?.getString("categoryName") ?: "" - remember(deeplinkName) { - val categories = databaseHelper.getBasics() - val category = categories.firstOrNull { it.getHtmlFileName() == deeplinkName } - category?.title ?: "Not found" - } - } else { - "" - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/AppInfoDialog.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/AppInfoDialog.kt deleted file mode 100644 index 649b9a7..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/AppInfoDialog.kt +++ /dev/null @@ -1,162 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens - -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Button -import androidx.compose.material.Card -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import com.inspiredandroid.linuxcommandbibliotheca.R -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme -import com.linuxcommandlibrary.shared.* - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -// Todo: Replace with AlertDialog - -@Composable -fun AppInfoDialog( - onDismiss: () -> Unit = {}, -) { - val uriHandler = LocalUriHandler.current - Dialog(onDismissRequest = { onDismiss() }) { - Card( - elevation = 8.dp, - shape = RoundedCornerShape(6.dp), - ) { - Column(modifier = Modifier.fillMaxSize()) { - Text( - stringResource(R.string.app_name), - style = MaterialTheme.typography.h5, - modifier = Modifier.padding(8.dp), - ) - Row { - Button( - modifier = Modifier.padding(start = 6.dp), - content = { - Text( - "Rate the app", - color = Color.White, - ) - }, - onClick = { - uriHandler.openUri("https://play.google.com/store/apps/details?id=com.inspiredandroid.linuxcommandbibliotheca") - }, - ) - IconButton(onClick = { - uriHandler.openUri("https://github.com/SimonSchubert/LinuxCommandLibrary") - }) { - Icon( - painterResource(R.drawable.ic_icons8_github), - contentDescription = "View on GitHub", // TODO: Use stringResource - modifier = Modifier.size(40.dp), - ) - } - } - Text( - "Version: ${Version.appVersion}", - style = MaterialTheme.typography.caption, - modifier = Modifier.padding(8.dp), - ) - Column( - modifier = Modifier - .padding(8.dp) - .weight(1f) - .verticalScroll(rememberScrollState()), - ) { - Text("Support this project", style = MaterialTheme.typography.h6) - Text("By using my referral links for these amazing products.") - Spacer(Modifier.height(4.dp)) - Image( - modifier = Modifier - .fillMaxWidth() - .clickable { - val link = "https://linuxcommandlibrary.com/proton-free-2025" - uriHandler.openUri(link) - }, - painter = painterResource(R.mipmap.proton_free_horizontal), - contentDescription = "Proton services promotion", // TODO: Use stringResource - ) - Spacer(Modifier.height(8.dp)) - Image( - modifier = Modifier - .fillMaxWidth() - .clickable { - val link = "https://linuxcommandlibrary.com/linode-2025" - uriHandler.openUri(link) - }, - painter = painterResource(R.mipmap.linode_horizontal), - contentDescription = "Linode cloud hosting promotion", // TODO: Use stringResource - ) - - Spacer(Modifier.height(8.dp)) - Text("Man pages", style = MaterialTheme.typography.h6) - Text("Licence information about the man page is usually specified in the man detail page under the category Author, Copyright or Licence. If there is no information on the page you can find the information in the man page source file on your linux system. If you have questions or can't find what you need, you can contact me at sschubert89@gmail.com.") - - Spacer(Modifier.height(8.dp)) - Text("TLDR pages", style = MaterialTheme.typography.h6) - Text( - "The MIT License (MIT) Copyright (c) 2014 the TLDR team and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", - ) - Spacer(Modifier.height(8.dp)) - Text( - "Thanks to commandlinefu.com for the one-liners and icons8.com for the icons", - style = MaterialTheme.typography.h6, - ) - } - TextButton( - content = { Text("OK") }, - modifier = Modifier - .align(Alignment.End) - .padding(end = 6.dp), - onClick = onDismiss, - ) - } - } - } -} - -@Preview -@Composable -fun AppInfoDialogPreview() { - LinuxTheme { - AppInfoDialog() - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/BookmarkFeedbackDialog.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/BookmarkFeedbackDialog.kt deleted file mode 100644 index 48159f2..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/BookmarkFeedbackDialog.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Card -import androidx.compose.material.Icon -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import com.inspiredandroid.linuxcommandbibliotheca.R -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.SectionTitle -import kotlinx.coroutines.delay - -@Composable -fun BookmarkFeedbackDialog(onDismiss: () -> Unit) { - LaunchedEffect(Unit) { - delay(600) - onDismiss() - } - - Dialog(onDismissRequest = onDismiss) { - Card( - elevation = 8.dp, - shape = RoundedCornerShape(6.dp), - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(16.dp), - ) { - Icon( - painterResource(R.drawable.ic_bookmark_black_24dp), - contentDescription = null, // Decorative, as title says "Bookmarked" - modifier = Modifier.size(48.dp), - ) - - Spacer(Modifier.height(8.dp)) - - SectionTitle(title = "Bookmarked") - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/InitializeDatabaseScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/InitializeDatabaseScreen.kt deleted file mode 100644 index 78dc3d9..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/InitializeDatabaseScreen.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.LinearProgressIndicator -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.R -import com.linuxcommandlibrary.shared.copyDatabase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -@Composable -fun InitializeDatabaseScreen(onFinish: () -> Unit = {}) { - var status by remember { - mutableIntStateOf(0) - } - val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current - - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Image( - painterResource(R.mipmap.ic_launcher_foreground), - contentDescription = null, - modifier = Modifier - .size(240.dp) - .align(Alignment.CenterHorizontally), - ) - Text( - "Initialize database", - color = MaterialTheme.colors.onSurface, - modifier = Modifier.align(Alignment.CenterHorizontally), - ) - LinearProgressIndicator( - progress = status.div(100f), - modifier = Modifier.padding(top = 16.dp), - ) - } - - LaunchedEffect(Unit) { - coroutineScope.launch(Dispatchers.IO) { - copyDatabase(context) { progress -> - status = progress - } - withContext(Dispatchers.Main) { - onFinish() - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basiccategories/BasicCategoriesScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basiccategories/BasicCategoriesScreen.kt deleted file mode 100644 index 3597de2..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basiccategories/BasicCategoriesScreen.kt +++ /dev/null @@ -1,69 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basiccategories - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.ListItem -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.getIconResource -import com.linuxcommandlibrary.shared.getHtmlFileName -import org.koin.androidx.compose.koinViewModel - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun BasicCategoriesScreen( - viewModel: BasicCategoriesViewModel = koinViewModel(), - onNavigate: (String) -> Unit, -) { - LazyVerticalGrid( - modifier = Modifier.fillMaxSize(), - columns = GridCells.Adaptive(minSize = 300.dp), - ) { - items( - items = viewModel.basicCategories, - key = { it.id }, - contentType = { "basic_category_item" }, - ) { basicCategory -> - ListItem( - text = { Text(basicCategory.title) }, - icon = { - Icon( - painterResource(basicCategory.getIconResource()), - contentDescription = null, - modifier = Modifier.size(40.dp), - ) - }, - modifier = Modifier.clickable { - onNavigate( - "basicgroups?categoryId=${basicCategory.id}&categoryName=${basicCategory.getHtmlFileName()}", - ) - }, - ) - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basiccategories/BasicCategoriesViewModel.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basiccategories/BasicCategoriesViewModel.kt deleted file mode 100644 index 0cab6be..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basiccategories/BasicCategoriesViewModel.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basiccategories - -import androidx.lifecycle.ViewModel -import com.linuxcommandlibrary.shared.databaseHelper -import databases.BasicCategory -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -class BasicCategoriesViewModel : ViewModel() { - - var basicCategories: ImmutableList = databaseHelper.getBasics().toImmutableList() -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsScreen.kt deleted file mode 100644 index ae0dd4d..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsScreen.kt +++ /dev/null @@ -1,114 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.ListItem -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.getIconResource -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.CommandView -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.HighlightedText -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.getCommandList -import databases.BasicGroup -import kotlinx.collections.immutable.toImmutableList -import org.koin.androidx.compose.koinViewModel -import org.koin.core.parameter.parametersOf - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun BasicGroupsScreen( - categoryId: Long, - viewModel: BasicGroupsViewModel = koinViewModel( - parameters = { parametersOf(categoryId) }, - ), - onNavigate: (String) -> Unit = {}, -) { - val uiState by viewModel.uiState.collectAsState() - - LazyColumn(Modifier.fillMaxSize()) { - items( - items = uiState.basicGroups, - key = { it.id }, - contentType = { "basic_group_item" }, - ) { basicGroup -> - BasicGroupColumn( - basicGroup = basicGroup, - isExpanded = !uiState.collapsedMap.getOrDefault(basicGroup.id, true), - onToggleCollapse = { viewModel.toggleCollapse(basicGroup.id) }, - onNavigate = onNavigate, - ) - } - } -} - -@Composable -fun BasicGroupColumn( - basicGroup: BasicGroup, - searchText: String = "", - isExpanded: Boolean, - onToggleCollapse: () -> Unit, - onNavigate: (String) -> Unit = {}, -) { - ListItem( - text = { - HighlightedText( - text = basicGroup.description, - pattern = searchText, - maxLines = 3, - ) - }, - icon = { - Icon( - painterResource(basicGroup.getIconResource()), - contentDescription = null, - modifier = Modifier.size(40.dp), - ) - }, - modifier = Modifier.clickable { onToggleCollapse() }, - ) - - if (isExpanded) { - ExpandedGroupContent(basicGroup = basicGroup, onNavigate = onNavigate) - } -} - -@Composable -private fun ExpandedGroupContent(basicGroup: BasicGroup, onNavigate: (String) -> Unit) { - val commands = remember(basicGroup.id) { - databaseHelper.getBasicCommands(basicGroup.id).toImmutableList() - } - commands.forEach { basicCommand -> - CommandView( - command = basicCommand.command, - elements = basicCommand.command.getCommandList(basicCommand.mans).toImmutableList(), - onNavigate = onNavigate, - ) - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsUiState.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsUiState.kt deleted file mode 100644 index ef047fe..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsUiState.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups - -import databases.BasicGroup -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableMap -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.persistentMapOf - -data class BasicGroupsUiState( - val basicGroups: ImmutableList = persistentListOf(), - val collapsedMap: ImmutableMap = persistentMapOf(), -) diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsViewModel.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsViewModel.kt deleted file mode 100644 index b5aec81..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsViewModel.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups - -import androidx.lifecycle.ViewModel -import com.linuxcommandlibrary.shared.databaseHelper -import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentMap -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -class BasicGroupsViewModel(categoryId: Long) : ViewModel() { - - private val _uiState = MutableStateFlow(BasicGroupsUiState()) - val uiState = _uiState.asStateFlow() - - init { - val groups = databaseHelper.getBasicGroupsByQuery(categoryId).toImmutableList() - _uiState.value = BasicGroupsUiState(basicGroups = groups) - } - - fun isGroupCollapsed(id: Long): Boolean = _uiState.value.collapsedMap.getOrDefault(id, true) - - fun toggleCollapse(id: Long) { - _uiState.update { currentState -> - val newMap = currentState.collapsedMap.toMutableMap() - newMap[id] = !currentState.collapsedMap.getOrDefault(id, true) - currentState.copy(collapsedMap = newMap.toPersistentMap()) - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailScreen.kt deleted file mode 100644 index f877f55..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailScreen.kt +++ /dev/null @@ -1,221 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class, ExperimentalLayoutApi::class) - -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commanddetail - -import android.text.style.StyleSpan -import android.text.style.UnderlineSpan -import androidx.activity.compose.LocalActivity -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.Chip -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ListItem -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.core.text.HtmlCompat -import androidx.lifecycle.ViewModelStoreOwner -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.CommandView -import com.linuxcommandlibrary.shared.CommandElement -import com.linuxcommandlibrary.shared.databaseHelper -import databases.CommandSection -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList -import org.koin.androidx.compose.koinViewModel -import org.koin.core.parameter.parametersOf - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun CommandDetailScreen( - commandId: Long, - viewModel: CommandDetailViewModel = koinViewModel( - key = commandId.toString(), - viewModelStoreOwner = LocalActivity.current as ViewModelStoreOwner, - parameters = { parametersOf(commandId) }, - ), - onNavigate: (String) -> Unit, -) { - val uiState by viewModel.state.collectAsStateWithLifecycle() - - LazyColumn(Modifier.fillMaxSize()) { - items( - items = uiState.sections, // This should ideally be ImmutableList from ViewModel - key = { it.id }, - contentType = { "command_section_item" }, - ) { section -> - CommandSectionColumn( - section = section, - isExpanded = uiState.expandedSectionsMap.getOrDefault(section.id, false), - onToggleExpanded = { id -> viewModel.onToggleExpanded(id) }, - onNavigate = onNavigate, - ) - } - } -} - -@Composable -private fun CommandSectionColumn( - section: CommandSection, - isExpanded: Boolean, - onToggleExpanded: (Long) -> Unit, - onNavigate: (String) -> Unit, -) { - ListItem( - text = { - Text( - section.title.uppercase(), - fontWeight = FontWeight.Bold, - fontSize = 20.sp, - ) - }, - modifier = Modifier.clickable { - onToggleExpanded(section.id) - }, - ) - - if (isExpanded) { - when (section.title) { - "TLDR" -> TldrSectionContent(content = section.content, onNavigate = onNavigate) - "SEE ALSO" -> SeeAlsoSectionContent(content = section.content, onNavigate = onNavigate) - else -> DefaultSectionContent(content = section.content) - } - } -} - -@Composable -private fun TldrSectionContent(content: String, onNavigate: (String) -> Unit) { - Column( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp), - ) { - val tldrParts = content.split("") - tldrParts.forEachIndexed { index, s -> - val split = s.split("") - if (split.size > 1) { - Text( - text = split[0], - fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f), - ) - - val command = "$ " + split[1].replace("
", "").replace("`", "") - CommandView( - command = command, - elements = listOf(CommandElement.Text(command)).toImmutableList(), - onNavigate = onNavigate, - verticalPadding = 4.dp, - ) - } - if (index != tldrParts.lastIndex && split.size > 1) { // Add spacer only if content was added - Spacer(Modifier.height(6.dp)) - } - } - } -} - -@Composable -private fun SeeAlsoSectionContent(content: String, onNavigate: (String) -> Unit) { - val commands = getCommands(content) - if (commands.isNotEmpty()) { - FlowRow( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - commands.forEach { name -> - Chip(onClick = { - onNavigate("command?commandName=$name") - }) { - Text( - text = name, - color = MaterialTheme.colors.onSurface, - ) - } - } - } - } else { - // fallback to default rendering if no commands were parsed (e.g. plain text) - DefaultSectionContent(content = content, isFallback = true) - } -} - -@Composable -private fun DefaultSectionContent(content: String, isFallback: Boolean = false) { - Text( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = if (isFallback) 0.dp else 8.dp, bottom = 8.dp), - text = content.toAnnotatedString(), - color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f), - ) -} - -private fun getCommands(input: String): ImmutableList { - val commands = input.split(",").map { it.trim() } - - return commands - .map { command -> - command.replace(Regex("\\(\\d+\\)$"), "").trim() - }.filter { - databaseHelper.getCommand(it) != null - }.toImmutableList() -} - -private fun String.toAnnotatedString(): AnnotatedString { - val spanned = HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_LEGACY) - - val trimmedText = spanned.toString().trim('\n', ' ') - - return buildAnnotatedString { - append(trimmedText) - - spanned.getSpans(0, trimmedText.length, Any::class.java).forEach { span -> - val start = spanned.getSpanStart(span) - val end = spanned.getSpanEnd(span) - - when (span) { - is StyleSpan -> when (span.style) { - android.graphics.Typeface.BOLD -> { - addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end) - } - android.graphics.Typeface.ITALIC -> { - addStyle(SpanStyle(fontStyle = FontStyle.Italic), start, end) - } - } - is UnderlineSpan -> { - addStyle(SpanStyle(textDecoration = TextDecoration.Underline), start, end) - } - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailUiState.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailUiState.kt deleted file mode 100644 index 8516238..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailUiState.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commanddetail - -import databases.CommandSection -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableMap - -data class CommandDetailUiState( - val sections: ImmutableList, - val expandedSectionsMap: ImmutableMap, - val isBookmarked: Boolean = false, - val showBookmarkDialog: Boolean = false, -) { - fun isAllExpanded(): Boolean = expandedSectionsMap.all { it.value } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailViewModel.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailViewModel.kt deleted file mode 100644 index 3b7eb43..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commanddetail/CommandDetailViewModel.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commanddetail - -import androidx.lifecycle.ViewModel -import com.inspiredandroid.linuxcommandbibliotheca.DataManager -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.getSortPriority -import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toImmutableMap -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -class CommandDetailViewModel( - private val commandId: Long, - private val dataManager: DataManager, -) : ViewModel() { - - val state: MutableStateFlow - - init { - val sectionsData = databaseHelper.getSections(commandId).sortedBy { it.getSortPriority() } - val isAutoExpandEnabled = dataManager.isAutoExpandSections() - state = MutableStateFlow( - CommandDetailUiState( - sections = sectionsData.toImmutableList(), - expandedSectionsMap = sectionsData.associate { - it.id to isAutoExpandEnabled - }.toImmutableMap(), - isBookmarked = dataManager.hasBookmark(commandId), - ), - ) - } - - fun onToggleAllExpanded() { - val isAllExpanded = state.value.isAllExpanded() - state.update { - val currentMap = it.expandedSectionsMap - val updatedMap = currentMap.toMutableMap() - updatedMap.replaceAll { _, _ -> !isAllExpanded } - it.copy(expandedSectionsMap = updatedMap.toImmutableMap()) - } - dataManager.setAutoExpandSections(!isAllExpanded) - } - - fun onToggleExpanded(id: Long) { - state.update { - val currentMap = it.expandedSectionsMap - val updatedMap = currentMap.toMutableMap() - updatedMap[id] = !updatedMap.getOrDefault(id, false) - it.copy(expandedSectionsMap = updatedMap.toImmutableMap()) - } - } - - fun removeBookmark() { - state.update { - it.copy(isBookmarked = false) - } - dataManager.removeBookmark(commandId) - } - - fun addBookmark() { - state.update { - it.copy( - isBookmarked = true, - showBookmarkDialog = true, - ) - } - dataManager.addBookmark(commandId) - } - - fun hideBookmarkDialog() { - state.update { - it.copy(showBookmarkDialog = false) - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commandlist/CommandListScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commandlist/CommandListScreen.kt deleted file mode 100644 index d0cdd5c..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commandlist/CommandListScreen.kt +++ /dev/null @@ -1,128 +0,0 @@ -@file:OptIn( - ExperimentalMaterialApi::class, - ExperimentalMaterialApi::class, - ExperimentalMaterialApi::class, -) - -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commandlist - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.ListItem -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.inspiredandroid.linuxcommandbibliotheca.R -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.HighlightedText -import com.inspiredandroid.linuxcommandbibliotheca.ui.theme.LinuxTheme -import databases.Command -import org.koin.androidx.compose.koinViewModel - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun CommandListScreen( - viewModel: CommandListViewModel = koinViewModel(), - onNavigate: (String) -> Unit, -) { - val commands by viewModel.commands.collectAsState() - - LazyColumn { - items( - items = commands, - key = { it.id }, - contentType = { "command_list_item" }, - ) { command -> - CommandListItem( - command = command, - onNavigate = onNavigate, - isBookmarked = viewModel.hasBookmark(command.id), - ) - } - } -} - -@Composable -fun CommandListItem( - command: Command, - searchText: String = "", - onNavigate: (String) -> Unit, - isBookmarked: Boolean, -) { - ListItem( - text = { - HighlightedText( - text = command.name, - pattern = searchText, - ) - }, - trailing = { - if (isBookmarked) { - Icon( - painterResource(R.drawable.ic_bookmark_black_24dp), - contentDescription = stringResource(R.string.remove_bookmark), - ) - } - }, - secondaryText = { - HighlightedText( - text = command.description, - pattern = searchText, - ) - }, - modifier = Modifier.clickable( - onClick = remember(command.id, command.name, onNavigate) { - { onNavigate("command?commandId=${command.id}&commandName=${command.name}") } - }, - ), - ) -} - -@Preview -@Composable -fun CommandListItemPreview() { - val command = Command(0L, 0L, "cowsay", "A talking cow says moo.") - LinuxTheme { - CommandListItem( - command = command, - searchText = "cow", - onNavigate = { }, - isBookmarked = true, - ) - } -} - -@Preview -@Composable -fun CommandListScreenPreview() { - // This preview is more complex due to the ViewModel dependency. - // For a proper preview, you'd typically use a fake ViewModel implementation - // or mock data source. For now, this will just show an empty screen - // or potentially crash if the ViewModel koinViewModel() can't be resolved in preview. - // To make it useful, one might need to adjust Koin setup for previews or pass - // a fake ViewModel. - // LinuxTheme { - // CommandListScreen(onNavigate = {}) - // } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commandlist/CommandListViewModel.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commandlist/CommandListViewModel.kt deleted file mode 100644 index b03c3c8..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/commandlist/CommandListViewModel.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commandlist - -import android.content.SharedPreferences -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.inspiredandroid.linuxcommandbibliotheca.DataManager -import com.linuxcommandlibrary.shared.databaseHelper -import databases.Command -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -class CommandListViewModel(private val dataManager: DataManager) : ViewModel() { - - private val _commands = MutableStateFlow>(persistentListOf()) - val commands = _commands.asStateFlow() - - private val preferenceListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> - if (key == DataManager.KEY_BOOKMARKS) { - updateCommands() - } - } - - init { - updateCommands() - dataManager.prefs.registerOnSharedPreferenceChangeListener(preferenceListener) - } - - private fun updateCommands() { - viewModelScope.launch(Dispatchers.IO) { - _commands.update { - databaseHelper.getCommands().sortedBy { !hasBookmark(it.id) }.toImmutableList() - } - } - } - - fun hasBookmark(id: Long): Boolean = dataManager.hasBookmark(id) - - override fun onCleared() { - dataManager.prefs.unregisterOnSharedPreferenceChangeListener(preferenceListener) - super.onCleared() - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchScreen.kt deleted file mode 100644 index fc9e0f1..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchScreen.kt +++ /dev/null @@ -1,97 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.search - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups.BasicGroupColumn -import com.inspiredandroid.linuxcommandbibliotheca.ui.screens.commandlist.CommandListItem -import org.koin.androidx.compose.koinViewModel - -@Composable -fun SearchScreen( - searchText: String, - viewModel: SearchViewModel = koinViewModel(), - onNavigate: (String) -> Unit, -) { - // Removed the early return for searchText.isEmpty() as ViewModel now handles it by emitting an empty state. - val uiState by viewModel.uiState.collectAsState() - - LaunchedEffect(searchText) { - viewModel.search(searchText) - } - - val keyboardController = LocalSoftwareKeyboardController.current - val lazyListState = rememberLazyListState() - - val showEmptyMessage by remember(uiState.filteredCommands, uiState.filteredBasicGroups) { - derivedStateOf { - uiState.filteredCommands.isEmpty() && uiState.filteredBasicGroups.isEmpty() - } - } - - // Only show "404" if search text is not empty and results are empty - if (searchText.isNotEmpty() && showEmptyMessage) { - Box( - modifier = Modifier - .fillMaxSize() - .clickable(enabled = false, onClick = {}) - .background(MaterialTheme.colors.background), - ) { - Text("404 command not found", modifier = Modifier.align(Alignment.Center)) - } - } else { - LazyColumn( - Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background), - state = lazyListState, - ) { - items( - items = uiState.filteredBasicGroups, - key = { "basicGroup_${it.id}" }, - contentType = { "search_basic_group_item" }, - ) { basicGroup -> - BasicGroupColumn( - basicGroup = basicGroup, - searchText = searchText, - onNavigate = onNavigate, - isExpanded = !uiState.collapsedMap.getOrDefault(basicGroup.id, false), - onToggleCollapse = { viewModel.toggleCollapse(basicGroup.id) }, - ) - } - items( - items = uiState.filteredCommands, - key = { "command_${it.id}" }, - contentType = { "search_command_item" }, - ) { command -> - CommandListItem( - command = command, - searchText = searchText, - onNavigate = onNavigate, - isBookmarked = false, // Or fetch actual bookmark status if relevant - ) - } - } - } - - LaunchedEffect(lazyListState.isScrollInProgress) { - if (lazyListState.isScrollInProgress) { - keyboardController?.hide() - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchUiState.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchUiState.kt deleted file mode 100644 index 6d71885..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchUiState.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.search - -import databases.BasicGroup -import databases.Command -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableMap -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.persistentMapOf - -data class SearchUiState( - val filteredCommands: ImmutableList = persistentListOf(), - val filteredBasicGroups: ImmutableList = persistentListOf(), - val collapsedMap: ImmutableMap = persistentMapOf(), -) diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchViewModel.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchViewModel.kt deleted file mode 100644 index 9f1cb82..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchViewModel.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.search - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.sortedSearch -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentMap -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.ensureActive -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import kotlin.coroutines.cancellation.CancellationException - -class SearchViewModel : ViewModel() { - - private val _uiState = MutableStateFlow(SearchUiState()) - val uiState = _uiState.asStateFlow() - - fun isGroupCollapsed(id: Long): Boolean = _uiState.value.collapsedMap.getOrDefault(id, false) - - fun toggleCollapse(id: Long) { - _uiState.update { currentState -> - val newMap = currentState.collapsedMap.toMutableMap() - newMap[id] = !currentState.collapsedMap.getOrDefault(id, false) - currentState.copy(collapsedMap = newMap.toPersistentMap()) - } - } - - private var searchJob: Job? = null - fun search(searchText: String) { - searchJob?.cancel() - if (searchText.isBlank()) { - _uiState.update { - it.copy( - filteredCommands = persistentListOf(), - filteredBasicGroups = persistentListOf(), - ) - } - return - } - searchJob = viewModelScope.launch(Dispatchers.IO) { - try { - ensureActive() - - val commands = databaseHelper.getCommandsByQuery(searchText).sortedSearch(searchText) - val basicGroups = databaseHelper.getBasicGroupsByQuery(searchText) - - ensureActive() - - _uiState.update { currentState -> - currentState.copy( - filteredCommands = commands.toImmutableList(), - filteredBasicGroups = basicGroups.toImmutableList(), - ) - } - } catch (ignore: CancellationException) { - // Preserve previous results on cancellation - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/tips/TipsScreen.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/tips/TipsScreen.kt deleted file mode 100644 index 81a91c7..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/tips/TipsScreen.kt +++ /dev/null @@ -1,111 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class) - -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.tips - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells -import androidx.compose.foundation.lazy.staggeredgrid.items -import androidx.compose.material.Card -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.CommandView -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.NestedCommandView -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.NestedText -import com.inspiredandroid.linuxcommandbibliotheca.ui.composables.SectionTitle -import kotlinx.collections.immutable.toImmutableList -import org.koin.androidx.compose.koinViewModel - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Composable -fun TipsScreen( - onNavigate: (String) -> Unit = {}, - viewModel: TipsViewModel = koinViewModel(), -) { - LazyVerticalStaggeredGrid( - modifier = Modifier - .semantics { contentDescription = "Scroll" }, - columns = StaggeredGridCells.Adaptive(minSize = 300.dp), - ) { - items( - items = viewModel.tips, - key = { it.tip.id }, - contentType = { "tip_item" }, - ) { tip -> - TipItemCard(tip = tip, onNavigate = onNavigate) - } - } -} - -@Composable -private fun TipItemCard(tip: MergedTip, onNavigate: (String) -> Unit) { - Card( - elevation = 4.dp, - modifier = Modifier - .padding(4.dp) - .fillMaxWidth(), - ) { - Column(modifier = Modifier.padding(8.dp)) { - SectionTitle(title = tip.tip.title) - TipSections(sections = tip.sections, onNavigate = onNavigate) - } - } -} - -@Composable -private fun TipSections(sections: List, onNavigate: (String) -> Unit) { - // Assuming MergedTip.sections is already ImmutableList from ViewModel refactor - // If not, it should be passed as ImmutableList - sections.forEach { element -> - when (element) { - is TipSectionElement.Text -> { - Text(element.text) - } - - is TipSectionElement.Code -> { - CommandView( - command = element.command, - elements = element.elements.toImmutableList(), // elements within TipSectionElement should also be ImmutableList - onNavigate = onNavigate, - ) - } - - is TipSectionElement.NestedCode -> { - NestedCommandView( - text = element.text, - command = element.command, - commandElements = element.elements.toImmutableList(), // elements within TipSectionElement should also be ImmutableList - onNavigate = onNavigate, - ) - } - - is TipSectionElement.NestedText -> { - NestedText( - textLeft = element.text, - textRight = element.info, - ) - } - } - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/tips/TipsViewModel.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/tips/TipsViewModel.kt deleted file mode 100644 index 1d76f4f..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/tips/TipsViewModel.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.tips - -import androidx.lifecycle.ViewModel -import com.linuxcommandlibrary.shared.CommandElement -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.getCommandList -import databases.Tip -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -sealed class TipSectionElement { - data class Text(val text: String) : TipSectionElement() - data class Code(val command: String, val elements: ImmutableList) : TipSectionElement() - - data class NestedCode( - val text: String, - val command: String, - val elements: ImmutableList, - ) : TipSectionElement() - - data class NestedText(val text: String, val info: String) : TipSectionElement() -} - -data class MergedTip(val tip: Tip, val sections: ImmutableList) - -class TipsViewModel : ViewModel() { - - var tips: ImmutableList - - init { - val tipSectionsFromDB = databaseHelper.getTipSections() // Renamed to avoid confusion with MergedTip.sections - tips = databaseHelper.getTips().map { tip -> - MergedTip( - tip, - tipSectionsFromDB.filter { it.tip_id == tip.id }.map { section -> - when (section.type) { - 0L -> { - val text = - section.data1.replace("\\n", "").replace("", "").replace("", "") - .replace("\\'", "") - TipSectionElement.Text(text) - } - - 1L -> { - TipSectionElement.Code( - section.data1, - section.data1.getCommandList(section.extra).toImmutableList(), - ) - } - - 3L -> { - if (section.data2.startsWith("$")) { - TipSectionElement.NestedCode( - section.data1, - section.data2, - section.data2.getCommandList(section.extra).toImmutableList(), - ) - } else { - TipSectionElement.NestedText(section.data1, section.data2) - } - } - - else -> { - TipSectionElement.Text("") - } - } - }.toImmutableList(), // Convert list of sections for a tip to ImmutableList - ) - }.toImmutableList() // Convert final list of MergedTip to ImmutableList - } -} diff --git a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/theme/LinuxTheme.kt b/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/theme/LinuxTheme.kt deleted file mode 100644 index 5bba69a..0000000 --- a/android/src/main/java/com/inspiredandroid/linuxcommandbibliotheca/ui/theme/LinuxTheme.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Typography -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.compositionLocalOf -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp -import com.inspiredandroid.linuxcommandbibliotheca.R - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -data class CustomColors( - val navBarBackground: Color = Color(0xFF00695C), -) - -val LocalCustomColors = compositionLocalOf { CustomColors() } - -@Composable -fun LinuxTheme(content: @Composable () -> Unit) { - val darkMode = isSystemInDarkTheme() - val darkColors = darkColors( - primary = Color(0xFFe45151), - secondary = Color.White, - background = Color(0xFF262626), - surface = Color(0xFF262626), - ) - val lightColors = lightColors( - primary = Color(0xFFe45151), - secondary = Color.Black, - background = Color.White, - surface = Color.White, - ) - val colorSchema = if (darkMode) darkColors else lightColors - - val techMonoFont = FontFamily( - Font(R.font.share_tech_mono), - ) - - val codeTextStyle = TextStyle( - fontFamily = techMonoFont, - fontWeight = FontWeight.Normal, - fontSize = 14.sp, - color = colorSchema.secondary, - ) - - val typography = Typography( - subtitle2 = codeTextStyle, - ) - - val customColors = if (darkMode) { - CustomColors( - navBarBackground = Color(0xFF2D2D2D), - ) - } else { - CustomColors( - navBarBackground = Color(0xFFFAFAFA), - ) - } - CompositionLocalProvider(LocalCustomColors provides customColors) { - MaterialTheme( - colors = colorSchema, - typography = typography, - content = content, - ) - } -} diff --git a/android/src/main/res/drawable/ic_add_rule.xml b/android/src/main/res/drawable/ic_add_rule.xml deleted file mode 100644 index 78ef5c4..0000000 --- a/android/src/main/res/drawable/ic_add_rule.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_add_user_to_group_white_48dp.xml b/android/src/main/res/drawable/ic_add_user_to_group_white_48dp.xml deleted file mode 100644 index 7c380be..0000000 --- a/android/src/main/res/drawable/ic_add_user_to_group_white_48dp.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_android_black_24dp.xml b/android/src/main/res/drawable/ic_android_black_24dp.xml deleted file mode 100644 index 0bf31ac..0000000 --- a/android/src/main/res/drawable/ic_android_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/android/src/main/res/drawable/ic_arrow_upward_black_24dp.xml b/android/src/main/res/drawable/ic_arrow_upward_black_24dp.xml deleted file mode 100644 index a8010ae..0000000 --- a/android/src/main/res/drawable/ic_arrow_upward_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_available_updates.xml b/android/src/main/res/drawable/ic_available_updates.xml deleted file mode 100644 index ba36b38..0000000 --- a/android/src/main/res/drawable/ic_available_updates.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_baseline_bookmark_24.xml b/android/src/main/res/drawable/ic_baseline_bookmark_24.xml deleted file mode 100644 index 78ef499..0000000 --- a/android/src/main/res/drawable/ic_baseline_bookmark_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_baseline_bookmark_border_24.xml b/android/src/main/res/drawable/ic_baseline_bookmark_border_24.xml deleted file mode 100644 index 56ae1c5..0000000 --- a/android/src/main/res/drawable/ic_baseline_bookmark_border_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_battery_90_black_24dp.xml b/android/src/main/res/drawable/ic_battery_90_black_24dp.xml deleted file mode 100644 index d370a44..0000000 --- a/android/src/main/res/drawable/ic_battery_90_black_24dp.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_bluetooth_black_24dp.xml b/android/src/main/res/drawable/ic_bluetooth_black_24dp.xml deleted file mode 100644 index b66af5a..0000000 --- a/android/src/main/res/drawable/ic_bluetooth_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_bluetooth_start.xml b/android/src/main/res/drawable/ic_bluetooth_start.xml deleted file mode 100644 index 401e444..0000000 --- a/android/src/main/res/drawable/ic_bluetooth_start.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_bookmark_black_24dp.xml b/android/src/main/res/drawable/ic_bookmark_black_24dp.xml deleted file mode 100644 index 3c36255..0000000 --- a/android/src/main/res/drawable/ic_bookmark_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_bookmark_border_black_24dp.xml b/android/src/main/res/drawable/ic_bookmark_border_black_24dp.xml deleted file mode 100644 index 65fc813..0000000 --- a/android/src/main/res/drawable/ic_bookmark_border_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_change_folder_white.xml b/android/src/main/res/drawable/ic_change_folder_white.xml deleted file mode 100644 index 33449da..0000000 --- a/android/src/main/res/drawable/ic_change_folder_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_check_black_24dp.xml b/android/src/main/res/drawable/ic_check_black_24dp.xml deleted file mode 100644 index 54f825f..0000000 --- a/android/src/main/res/drawable/ic_check_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_code_white_48dp.xml b/android/src/main/res/drawable/ic_code_white_48dp.xml deleted file mode 100644 index 4c00ca8..0000000 --- a/android/src/main/res/drawable/ic_code_white_48dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_collapse_all.xml b/android/src/main/res/drawable/ic_collapse_all.xml deleted file mode 100644 index 0c3a390..0000000 --- a/android/src/main/res/drawable/ic_collapse_all.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_create_file_white.xml b/android/src/main/res/drawable/ic_create_file_white.xml deleted file mode 100644 index dd3b845..0000000 --- a/android/src/main/res/drawable/ic_create_file_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_create_new_folder_white.xml b/android/src/main/res/drawable/ic_create_new_folder_white.xml deleted file mode 100644 index a0d4b91..0000000 --- a/android/src/main/res/drawable/ic_create_new_folder_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_delete_black_24dp.xml b/android/src/main/res/drawable/ic_delete_black_24dp.xml deleted file mode 100644 index 85d559d..0000000 --- a/android/src/main/res/drawable/ic_delete_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_delete_file_white.xml b/android/src/main/res/drawable/ic_delete_file_white.xml deleted file mode 100644 index 513c2e7..0000000 --- a/android/src/main/res/drawable/ic_delete_file_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_delete_folder_white_48dp.xml b/android/src/main/res/drawable/ic_delete_folder_white_48dp.xml deleted file mode 100644 index de3b171..0000000 --- a/android/src/main/res/drawable/ic_delete_folder_white_48dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_desktop_windows_black_24dp.xml b/android/src/main/res/drawable/ic_desktop_windows_black_24dp.xml deleted file mode 100644 index 1931c61..0000000 --- a/android/src/main/res/drawable/ic_desktop_windows_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_dns_black_24dp.xml b/android/src/main/res/drawable/ic_dns_black_24dp.xml deleted file mode 100644 index baf9b16..0000000 --- a/android/src/main/res/drawable/ic_dns_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_edit_group.xml b/android/src/main/res/drawable/ic_edit_group.xml deleted file mode 100644 index 41ea8b4..0000000 --- a/android/src/main/res/drawable/ic_edit_group.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_electronics.xml b/android/src/main/res/drawable/ic_electronics.xml deleted file mode 100644 index 7146154..0000000 --- a/android/src/main/res/drawable/ic_electronics.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_expand_all.xml b/android/src/main/res/drawable/ic_expand_all.xml deleted file mode 100644 index 3842734..0000000 --- a/android/src/main/res/drawable/ic_expand_all.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_file.xml b/android/src/main/res/drawable/ic_file.xml deleted file mode 100644 index d34a190..0000000 --- a/android/src/main/res/drawable/ic_file.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_file_content_white.xml b/android/src/main/res/drawable/ic_file_content_white.xml deleted file mode 100644 index cd2f9c5..0000000 --- a/android/src/main/res/drawable/ic_file_content_white.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_file_copy_white_48dp.xml b/android/src/main/res/drawable/ic_file_copy_white_48dp.xml deleted file mode 100644 index 9983dad..0000000 --- a/android/src/main/res/drawable/ic_file_copy_white_48dp.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_file_download_black_24dp.xml b/android/src/main/res/drawable/ic_file_download_black_24dp.xml deleted file mode 100644 index fe2d559..0000000 --- a/android/src/main/res/drawable/ic_file_download_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_file_download_white.xml b/android/src/main/res/drawable/ic_file_download_white.xml deleted file mode 100644 index e5f0bc0..0000000 --- a/android/src/main/res/drawable/ic_file_download_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_file_edit_white_48dp.xml b/android/src/main/res/drawable/ic_file_edit_white_48dp.xml deleted file mode 100644 index fc2e655..0000000 --- a/android/src/main/res/drawable/ic_file_edit_white_48dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_file_link_white_48dp.xml b/android/src/main/res/drawable/ic_file_link_white_48dp.xml deleted file mode 100644 index 4794c93..0000000 --- a/android/src/main/res/drawable/ic_file_link_white_48dp.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_file_move_white.xml b/android/src/main/res/drawable/ic_file_move_white.xml deleted file mode 100644 index c678610..0000000 --- a/android/src/main/res/drawable/ic_file_move_white.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_file_permission_white_48dp.xml b/android/src/main/res/drawable/ic_file_permission_white_48dp.xml deleted file mode 100644 index e7fd86a..0000000 --- a/android/src/main/res/drawable/ic_file_permission_white_48dp.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_fingerprint_black_24dp.xml b/android/src/main/res/drawable/ic_fingerprint_black_24dp.xml deleted file mode 100644 index 1b5062a..0000000 --- a/android/src/main/res/drawable/ic_fingerprint_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_flash_on_black_24dp.xml b/android/src/main/res/drawable/ic_flash_on_black_24dp.xml deleted file mode 100644 index 29ceb33..0000000 --- a/android/src/main/res/drawable/ic_flash_on_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_folder_black_40dp.xml b/android/src/main/res/drawable/ic_folder_black_40dp.xml deleted file mode 100644 index 858fe0a..0000000 --- a/android/src/main/res/drawable/ic_folder_black_40dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_folder_list_white.xml b/android/src/main/res/drawable/ic_folder_list_white.xml deleted file mode 100644 index 03d63a5..0000000 --- a/android/src/main/res/drawable/ic_folder_list_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_folder_path_white.xml b/android/src/main/res/drawable/ic_folder_path_white.xml deleted file mode 100644 index 5af9b6b..0000000 --- a/android/src/main/res/drawable/ic_folder_path_white.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_healing_black_24dp.xml b/android/src/main/res/drawable/ic_healing_black_24dp.xml deleted file mode 100644 index 56a526f..0000000 --- a/android/src/main/res/drawable/ic_healing_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_bitcoin.xml b/android/src/main/res/drawable/ic_icon_bitcoin.xml deleted file mode 100644 index ad37068..0000000 --- a/android/src/main/res/drawable/ic_icon_bitcoin.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_controller.xml b/android/src/main/res/drawable/ic_icon_controller.xml deleted file mode 100644 index 81a59fa..0000000 --- a/android/src/main/res/drawable/ic_icon_controller.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_emacs.xml b/android/src/main/res/drawable/ic_icon_emacs.xml deleted file mode 100644 index 7604df7..0000000 --- a/android/src/main/res/drawable/ic_icon_emacs.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_icon_fun.xml b/android/src/main/res/drawable/ic_icon_fun.xml deleted file mode 100644 index 1cc59b4..0000000 --- a/android/src/main/res/drawable/ic_icon_fun.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_git.xml b/android/src/main/res/drawable/ic_icon_git.xml deleted file mode 100644 index 2a5f570..0000000 --- a/android/src/main/res/drawable/ic_icon_git.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_json.xml b/android/src/main/res/drawable/ic_icon_json.xml deleted file mode 100644 index fab1f87..0000000 --- a/android/src/main/res/drawable/ic_icon_json.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_mouse.xml b/android/src/main/res/drawable/ic_icon_mouse.xml deleted file mode 100644 index 6d6bfc8..0000000 --- a/android/src/main/res/drawable/ic_icon_mouse.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icon_skull.xml b/android/src/main/res/drawable/ic_icon_skull.xml deleted file mode 100644 index 37268ed..0000000 --- a/android/src/main/res/drawable/ic_icon_skull.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icon_system_task.xml b/android/src/main/res/drawable/ic_icon_system_task.xml deleted file mode 100644 index 832ac9d..0000000 --- a/android/src/main/res/drawable/ic_icon_system_task.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icon_user.xml b/android/src/main/res/drawable/ic_icon_user.xml deleted file mode 100644 index b80c921..0000000 --- a/android/src/main/res/drawable/ic_icon_user.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icon_vim.xml b/android/src/main/res/drawable/ic_icon_vim.xml deleted file mode 100644 index c51b8e2..0000000 --- a/android/src/main/res/drawable/ic_icon_vim.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_add_trash.xml b/android/src/main/res/drawable/ic_icons8_add_trash.xml deleted file mode 100644 index da7d986..0000000 --- a/android/src/main/res/drawable/ic_icons8_add_trash.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_add_user.xml b/android/src/main/res/drawable/ic_icons8_add_user.xml deleted file mode 100644 index a3b5737..0000000 --- a/android/src/main/res/drawable/ic_icons8_add_user.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_add_user_group.xml b/android/src/main/res/drawable/ic_icons8_add_user_group.xml deleted file mode 100644 index 31981fc..0000000 --- a/android/src/main/res/drawable/ic_icons8_add_user_group.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_arrow.xml b/android/src/main/res/drawable/ic_icons8_arrow.xml deleted file mode 100644 index 827a527..0000000 --- a/android/src/main/res/drawable/ic_icons8_arrow.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_bot.xml b/android/src/main/res/drawable/ic_icons8_bot.xml deleted file mode 100644 index e6f71f3..0000000 --- a/android/src/main/res/drawable/ic_icons8_bot.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_calendar_1.xml b/android/src/main/res/drawable/ic_icons8_calendar_1.xml deleted file mode 100644 index dfebe31..0000000 --- a/android/src/main/res/drawable/ic_icons8_calendar_1.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_cancel.xml b/android/src/main/res/drawable/ic_icons8_cancel.xml deleted file mode 100644 index 23ed847..0000000 --- a/android/src/main/res/drawable/ic_icons8_cancel.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_change_user_male.xml b/android/src/main/res/drawable/ic_icons8_change_user_male.xml deleted file mode 100644 index cec2192..0000000 --- a/android/src/main/res/drawable/ic_icons8_change_user_male.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_circled_pause.xml b/android/src/main/res/drawable/ic_icons8_circled_pause.xml deleted file mode 100644 index 40f58c1..0000000 --- a/android/src/main/res/drawable/ic_icons8_circled_pause.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_circled_play.xml b/android/src/main/res/drawable/ic_icons8_circled_play.xml deleted file mode 100644 index 4641ea3..0000000 --- a/android/src/main/res/drawable/ic_icons8_circled_play.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_clipboard.xml b/android/src/main/res/drawable/ic_icons8_clipboard.xml deleted file mode 100644 index 209d362..0000000 --- a/android/src/main/res/drawable/ic_icons8_clipboard.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_coin_wallet.xml b/android/src/main/res/drawable/ic_icons8_coin_wallet.xml deleted file mode 100644 index 060d9bc..0000000 --- a/android/src/main/res/drawable/ic_icons8_coin_wallet.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_compass.xml b/android/src/main/res/drawable/ic_icons8_compass.xml deleted file mode 100644 index e2c4269..0000000 --- a/android/src/main/res/drawable/ic_icons8_compass.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_connected.xml b/android/src/main/res/drawable/ic_icons8_connected.xml deleted file mode 100644 index 65ecd7a..0000000 --- a/android/src/main/res/drawable/ic_icons8_connected.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_console.xml b/android/src/main/res/drawable/ic_icons8_console.xml deleted file mode 100644 index d77790c..0000000 --- a/android/src/main/res/drawable/ic_icons8_console.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_copy.xml b/android/src/main/res/drawable/ic_icons8_copy.xml deleted file mode 100644 index 5520aef..0000000 --- a/android/src/main/res/drawable/ic_icons8_copy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_delete_trash.xml b/android/src/main/res/drawable/ic_icons8_delete_trash.xml deleted file mode 100644 index a2c336d..0000000 --- a/android/src/main/res/drawable/ic_icons8_delete_trash.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_document.xml b/android/src/main/res/drawable/ic_icons8_document.xml deleted file mode 100644 index bc691cd..0000000 --- a/android/src/main/res/drawable/ic_icons8_document.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_downloads_folder.xml b/android/src/main/res/drawable/ic_icons8_downloads_folder.xml deleted file mode 100644 index bd39bc6..0000000 --- a/android/src/main/res/drawable/ic_icons8_downloads_folder.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_edit_user.xml b/android/src/main/res/drawable/ic_icons8_edit_user.xml deleted file mode 100644 index 58f1383..0000000 --- a/android/src/main/res/drawable/ic_icons8_edit_user.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_exe.xml b/android/src/main/res/drawable/ic_icons8_exe.xml deleted file mode 100644 index 775df1e..0000000 --- a/android/src/main/res/drawable/ic_icons8_exe.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_file_preview.xml b/android/src/main/res/drawable/ic_icons8_file_preview.xml deleted file mode 100644 index 53c82b8..0000000 --- a/android/src/main/res/drawable/ic_icons8_file_preview.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_flow_chart.xml b/android/src/main/res/drawable/ic_icons8_flow_chart.xml deleted file mode 100644 index e111a91..0000000 --- a/android/src/main/res/drawable/ic_icons8_flow_chart.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_github.xml b/android/src/main/res/drawable/ic_icons8_github.xml deleted file mode 100644 index 4fce8e1..0000000 --- a/android/src/main/res/drawable/ic_icons8_github.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_golden_fever.xml b/android/src/main/res/drawable/ic_icons8_golden_fever.xml deleted file mode 100644 index 456430a..0000000 --- a/android/src/main/res/drawable/ic_icons8_golden_fever.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_group_foreground_selected.xml b/android/src/main/res/drawable/ic_icons8_group_foreground_selected.xml deleted file mode 100644 index da4b5c4..0000000 --- a/android/src/main/res/drawable/ic_icons8_group_foreground_selected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_hand_with_pen.xml b/android/src/main/res/drawable/ic_icons8_hand_with_pen.xml deleted file mode 100644 index f19002f..0000000 --- a/android/src/main/res/drawable/ic_icons8_hand_with_pen.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_hdd.xml b/android/src/main/res/drawable/ic_icons8_hdd.xml deleted file mode 100644 index 3923375..0000000 --- a/android/src/main/res/drawable/ic_icons8_hdd.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_hide.xml b/android/src/main/res/drawable/ic_icons8_hide.xml deleted file mode 100644 index 61fcebc..0000000 --- a/android/src/main/res/drawable/ic_icons8_hide.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_home.xml b/android/src/main/res/drawable/ic_icons8_home.xml deleted file mode 100644 index eb200cc..0000000 --- a/android/src/main/res/drawable/ic_icons8_home.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_keyboard.xml b/android/src/main/res/drawable/ic_icons8_keyboard.xml deleted file mode 100644 index 9274d56..0000000 --- a/android/src/main/res/drawable/ic_icons8_keyboard.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_kitchen_scales.xml b/android/src/main/res/drawable/ic_icons8_kitchen_scales.xml deleted file mode 100644 index d9a4253..0000000 --- a/android/src/main/res/drawable/ic_icons8_kitchen_scales.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_linux.xml b/android/src/main/res/drawable/ic_icons8_linux.xml deleted file mode 100644 index bded048..0000000 --- a/android/src/main/res/drawable/ic_icons8_linux.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_merge.xml b/android/src/main/res/drawable/ic_icons8_merge.xml deleted file mode 100644 index a0b283b..0000000 --- a/android/src/main/res/drawable/ic_icons8_merge.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_moderator_male.xml b/android/src/main/res/drawable/ic_icons8_moderator_male.xml deleted file mode 100644 index 2e9bbf9..0000000 --- a/android/src/main/res/drawable/ic_icons8_moderator_male.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_mother.xml b/android/src/main/res/drawable/ic_icons8_mother.xml deleted file mode 100644 index 78541bf..0000000 --- a/android/src/main/res/drawable/ic_icons8_mother.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_network_card.xml b/android/src/main/res/drawable/ic_icons8_network_card.xml deleted file mode 100644 index 5c025ac..0000000 --- a/android/src/main/res/drawable/ic_icons8_network_card.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_new.xml b/android/src/main/res/drawable/ic_icons8_new.xml deleted file mode 100644 index 09b39f3..0000000 --- a/android/src/main/res/drawable/ic_icons8_new.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_numbered_list.xml b/android/src/main/res/drawable/ic_icons8_numbered_list.xml deleted file mode 100644 index adbb3e6..0000000 --- a/android/src/main/res/drawable/ic_icons8_numbered_list.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_ping_pong.xml b/android/src/main/res/drawable/ic_icons8_ping_pong.xml deleted file mode 100644 index 2e1c8d7..0000000 --- a/android/src/main/res/drawable/ic_icons8_ping_pong.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_plus.xml b/android/src/main/res/drawable/ic_icons8_plus.xml deleted file mode 100644 index 3ebe830..0000000 --- a/android/src/main/res/drawable/ic_icons8_plus.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_print.xml b/android/src/main/res/drawable/ic_icons8_print.xml deleted file mode 100644 index b4bf1b7..0000000 --- a/android/src/main/res/drawable/ic_icons8_print.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_print_file.xml b/android/src/main/res/drawable/ic_icons8_print_file.xml deleted file mode 100644 index 5221af2..0000000 --- a/android/src/main/res/drawable/ic_icons8_print_file.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_redo.xml b/android/src/main/res/drawable/ic_icons8_redo.xml deleted file mode 100644 index 26df61b..0000000 --- a/android/src/main/res/drawable/ic_icons8_redo.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_remove_user_male.xml b/android/src/main/res/drawable/ic_icons8_remove_user_male.xml deleted file mode 100644 index bed659f..0000000 --- a/android/src/main/res/drawable/ic_icons8_remove_user_male.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_restore_window.xml b/android/src/main/res/drawable/ic_icons8_restore_window.xml deleted file mode 100644 index 02f87b3..0000000 --- a/android/src/main/res/drawable/ic_icons8_restore_window.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_reuse.xml b/android/src/main/res/drawable/ic_icons8_reuse.xml deleted file mode 100644 index a1c06c5..0000000 --- a/android/src/main/res/drawable/ic_icons8_reuse.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_root_server.xml b/android/src/main/res/drawable/ic_icons8_root_server.xml deleted file mode 100644 index 7a418b4..0000000 --- a/android/src/main/res/drawable/ic_icons8_root_server.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_save.xml b/android/src/main/res/drawable/ic_icons8_save.xml deleted file mode 100644 index 66438f6..0000000 --- a/android/src/main/res/drawable/ic_icons8_save.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_show_property.xml b/android/src/main/res/drawable/ic_icons8_show_property.xml deleted file mode 100644 index 449e013..0000000 --- a/android/src/main/res/drawable/ic_icons8_show_property.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_synchronize.xml b/android/src/main/res/drawable/ic_icons8_synchronize.xml deleted file mode 100644 index a198047..0000000 --- a/android/src/main/res/drawable/ic_icons8_synchronize.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_talk.xml b/android/src/main/res/drawable/ic_icons8_talk.xml deleted file mode 100644 index 2e30f79..0000000 --- a/android/src/main/res/drawable/ic_icons8_talk.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_teacher_hiring.xml b/android/src/main/res/drawable/ic_icons8_teacher_hiring.xml deleted file mode 100644 index 05f8ee7..0000000 --- a/android/src/main/res/drawable/ic_icons8_teacher_hiring.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_text.xml b/android/src/main/res/drawable/ic_icons8_text.xml deleted file mode 100644 index 624f64e..0000000 --- a/android/src/main/res/drawable/ic_icons8_text.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_treatment.xml b/android/src/main/res/drawable/ic_icons8_treatment.xml deleted file mode 100644 index d391687..0000000 --- a/android/src/main/res/drawable/ic_icons8_treatment.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_tv_off.xml b/android/src/main/res/drawable/ic_icons8_tv_off.xml deleted file mode 100644 index 76393f3..0000000 --- a/android/src/main/res/drawable/ic_icons8_tv_off.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_tv_on.xml b/android/src/main/res/drawable/ic_icons8_tv_on.xml deleted file mode 100644 index cc596e0..0000000 --- a/android/src/main/res/drawable/ic_icons8_tv_on.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_icons8_undo.xml b/android/src/main/res/drawable/ic_icons8_undo.xml deleted file mode 100644 index 53418d7..0000000 --- a/android/src/main/res/drawable/ic_icons8_undo.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_user_folder.xml b/android/src/main/res/drawable/ic_icons8_user_folder.xml deleted file mode 100644 index a677f47..0000000 --- a/android/src/main/res/drawable/ic_icons8_user_folder.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_user_male_circle.xml b/android/src/main/res/drawable/ic_icons8_user_male_circle.xml deleted file mode 100644 index f437867..0000000 --- a/android/src/main/res/drawable/ic_icons8_user_male_circle.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_icons8_visible.xml b/android/src/main/res/drawable/ic_icons8_visible.xml deleted file mode 100644 index e5c7bfa..0000000 --- a/android/src/main/res/drawable/ic_icons8_visible.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_icons8_work.xml b/android/src/main/res/drawable/ic_icons8_work.xml deleted file mode 100644 index 0a8d85e..0000000 --- a/android/src/main/res/drawable/ic_icons8_work.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_idea.xml b/android/src/main/res/drawable/ic_idea.xml deleted file mode 100644 index 5cca8b4..0000000 --- a/android/src/main/res/drawable/ic_idea.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_info_40dp.xml b/android/src/main/res/drawable/ic_info_40dp.xml deleted file mode 100644 index 62c8922..0000000 --- a/android/src/main/res/drawable/ic_info_40dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_info_black_24dp.xml b/android/src/main/res/drawable/ic_info_black_24dp.xml deleted file mode 100644 index 62c8922..0000000 --- a/android/src/main/res/drawable/ic_info_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_ip_address.xml b/android/src/main/res/drawable/ic_ip_address.xml deleted file mode 100644 index 6a7ecc0..0000000 --- a/android/src/main/res/drawable/ic_ip_address.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_keyboard_arrow_down_black_24dp.xml b/android/src/main/res/drawable/ic_keyboard_arrow_down_black_24dp.xml deleted file mode 100644 index 4dc68f4..0000000 --- a/android/src/main/res/drawable/ic_keyboard_arrow_down_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml b/android/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml deleted file mode 100644 index 0d0881d..0000000 --- a/android/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_keyboard_black_24dp.xml b/android/src/main/res/drawable/ic_keyboard_black_24dp.xml deleted file mode 100644 index b00c708..0000000 --- a/android/src/main/res/drawable/ic_keyboard_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_list_groups_white_48dp.xml b/android/src/main/res/drawable/ic_list_groups_white_48dp.xml deleted file mode 100644 index 75bed83..0000000 --- a/android/src/main/res/drawable/ic_list_groups_white_48dp.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_list_interfaces_white_48dp.xml b/android/src/main/res/drawable/ic_list_interfaces_white_48dp.xml deleted file mode 100644 index 1269650..0000000 --- a/android/src/main/res/drawable/ic_list_interfaces_white_48dp.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_list_sockets_white_48dp.xml b/android/src/main/res/drawable/ic_list_sockets_white_48dp.xml deleted file mode 100644 index f933d9d..0000000 --- a/android/src/main/res/drawable/ic_list_sockets_white_48dp.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_list_user_white_48dp.xml b/android/src/main/res/drawable/ic_list_user_white_48dp.xml deleted file mode 100644 index cddff0b..0000000 --- a/android/src/main/res/drawable/ic_list_user_white_48dp.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_loupe_black_24dp.xml b/android/src/main/res/drawable/ic_loupe_black_24dp.xml deleted file mode 100644 index a75df65..0000000 --- a/android/src/main/res/drawable/ic_loupe_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_memory_slot.xml b/android/src/main/res/drawable/ic_memory_slot.xml deleted file mode 100644 index 1c64248..0000000 --- a/android/src/main/res/drawable/ic_memory_slot.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_network_card_40dp.xml b/android/src/main/res/drawable/ic_network_card_40dp.xml deleted file mode 100644 index 5c025ac..0000000 --- a/android/src/main/res/drawable/ic_network_card_40dp.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_org_genocide.xml b/android/src/main/res/drawable/ic_org_genocide.xml deleted file mode 100644 index 6e3bf11..0000000 --- a/android/src/main/res/drawable/ic_org_genocide.xml +++ /dev/null @@ -1,2606 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_package_40.xml b/android/src/main/res/drawable/ic_package_40.xml deleted file mode 100644 index 02bc142..0000000 --- a/android/src/main/res/drawable/ic_package_40.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_power_settings_new_black_24dp.xml b/android/src/main/res/drawable/ic_power_settings_new_black_24dp.xml deleted file mode 100644 index 8065a58..0000000 --- a/android/src/main/res/drawable/ic_power_settings_new_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_public_black_24dp.xml b/android/src/main/res/drawable/ic_public_black_24dp.xml deleted file mode 100644 index 22788a0..0000000 --- a/android/src/main/res/drawable/ic_public_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_puzzle.xml b/android/src/main/res/drawable/ic_puzzle.xml deleted file mode 100644 index dfc047e..0000000 --- a/android/src/main/res/drawable/ic_puzzle.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_refresh_black_24dp.xml b/android/src/main/res/drawable/ic_refresh_black_24dp.xml deleted file mode 100644 index 9f9c433..0000000 --- a/android/src/main/res/drawable/ic_refresh_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_remote.xml b/android/src/main/res/drawable/ic_remote.xml deleted file mode 100644 index 7f661df..0000000 --- a/android/src/main/res/drawable/ic_remote.xml +++ /dev/null @@ -1,990 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_remove_user_from_group_white_48dp.xml b/android/src/main/res/drawable/ic_remove_user_from_group_white_48dp.xml deleted file mode 100644 index 3dd3ee4..0000000 --- a/android/src/main/res/drawable/ic_remove_user_from_group_white_48dp.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_remove_user_group.xml b/android/src/main/res/drawable/ic_remove_user_group.xml deleted file mode 100644 index 0d50345..0000000 --- a/android/src/main/res/drawable/ic_remove_user_group.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_report_black_24dp.xml b/android/src/main/res/drawable/ic_report_black_24dp.xml deleted file mode 100644 index fc2d15a..0000000 --- a/android/src/main/res/drawable/ic_report_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_search_40dp.xml b/android/src/main/res/drawable/ic_search_40dp.xml deleted file mode 100644 index 17e259b..0000000 --- a/android/src/main/res/drawable/ic_search_40dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_search_black_24dp.xml b/android/src/main/res/drawable/ic_search_black_24dp.xml deleted file mode 100644 index 17e259b..0000000 --- a/android/src/main/res/drawable/ic_search_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_search_executeable_man_white_48dp.xml b/android/src/main/res/drawable/ic_search_executeable_man_white_48dp.xml deleted file mode 100644 index b816d62..0000000 --- a/android/src/main/res/drawable/ic_search_executeable_man_white_48dp.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_search_history_white_48dp.xml b/android/src/main/res/drawable/ic_search_history_white_48dp.xml deleted file mode 100644 index 8661239..0000000 --- a/android/src/main/res/drawable/ic_search_history_white_48dp.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_search_in_file_white_48dp.xml b/android/src/main/res/drawable/ic_search_in_file_white_48dp.xml deleted file mode 100644 index b04442a..0000000 --- a/android/src/main/res/drawable/ic_search_in_file_white_48dp.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_search_list_white_48dp.xml b/android/src/main/res/drawable/ic_search_list_white_48dp.xml deleted file mode 100644 index 985ada3..0000000 --- a/android/src/main/res/drawable/ic_search_list_white_48dp.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_search_source_man_white_48dp.xml b/android/src/main/res/drawable/ic_search_source_man_white_48dp.xml deleted file mode 100644 index b1d9d4a..0000000 --- a/android/src/main/res/drawable/ic_search_source_man_white_48dp.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_security_black_24dp.xml b/android/src/main/res/drawable/ic_security_black_24dp.xml deleted file mode 100644 index 00579c2..0000000 --- a/android/src/main/res/drawable/ic_security_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_settings_black_24dp.xml b/android/src/main/res/drawable/ic_settings_black_24dp.xml deleted file mode 100644 index aaeae6b..0000000 --- a/android/src/main/res/drawable/ic_settings_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_settings_black_40dp.xml b/android/src/main/res/drawable/ic_settings_black_40dp.xml deleted file mode 100644 index aaeae6b..0000000 --- a/android/src/main/res/drawable/ic_settings_black_40dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_share_black_24dp.xml b/android/src/main/res/drawable/ic_share_black_24dp.xml deleted file mode 100644 index 7f2829b..0000000 --- a/android/src/main/res/drawable/ic_share_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_skull_40dp.xml b/android/src/main/res/drawable/ic_skull_40dp.xml deleted file mode 100644 index d3c05b9..0000000 --- a/android/src/main/res/drawable/ic_skull_40dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_stop_bluetooth.xml b/android/src/main/res/drawable/ic_stop_bluetooth.xml deleted file mode 100644 index 1397a85..0000000 --- a/android/src/main/res/drawable/ic_stop_bluetooth.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_stop_wifi.xml b/android/src/main/res/drawable/ic_stop_wifi.xml deleted file mode 100644 index 47395d4..0000000 --- a/android/src/main/res/drawable/ic_stop_wifi.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_storage_black_24dp.xml b/android/src/main/res/drawable/ic_storage_black_24dp.xml deleted file mode 100644 index 28cecff..0000000 --- a/android/src/main/res/drawable/ic_storage_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_switch_video_white_48dp.xml b/android/src/main/res/drawable/ic_switch_video_white_48dp.xml deleted file mode 100644 index 56c53eb..0000000 --- a/android/src/main/res/drawable/ic_switch_video_white_48dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_timer_black_24dp.xml b/android/src/main/res/drawable/ic_timer_black_24dp.xml deleted file mode 100644 index 56d6c02..0000000 --- a/android/src/main/res/drawable/ic_timer_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_toolbar_search_black_24dp.xml b/android/src/main/res/drawable/ic_toolbar_search_black_24dp.xml deleted file mode 100644 index 2ba4cf3..0000000 --- a/android/src/main/res/drawable/ic_toolbar_search_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_usb_black_48dp.xml b/android/src/main/res/drawable/ic_usb_black_48dp.xml deleted file mode 100644 index b8e73d2..0000000 --- a/android/src/main/res/drawable/ic_usb_black_48dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_user_male_circle_40dp.xml b/android/src/main/res/drawable/ic_user_male_circle_40dp.xml deleted file mode 100644 index 2f2ccf2..0000000 --- a/android/src/main/res/drawable/ic_user_male_circle_40dp.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_user_password.xml b/android/src/main/res/drawable/ic_user_password.xml deleted file mode 100644 index c0b4038..0000000 --- a/android/src/main/res/drawable/ic_user_password.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/android/src/main/res/drawable/ic_video_trimming_40dp.xml b/android/src/main/res/drawable/ic_video_trimming_40dp.xml deleted file mode 100644 index c62719a..0000000 --- a/android/src/main/res/drawable/ic_video_trimming_40dp.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_videogame_asset_black_24dp.xml b/android/src/main/res/drawable/ic_videogame_asset_black_24dp.xml deleted file mode 100644 index 461ff4c..0000000 --- a/android/src/main/res/drawable/ic_videogame_asset_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_videogame_asset_black_40dp.xml b/android/src/main/res/drawable/ic_videogame_asset_black_40dp.xml deleted file mode 100644 index 461ff4c..0000000 --- a/android/src/main/res/drawable/ic_videogame_asset_black_40dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_vip_lookup_white_48dp.xml b/android/src/main/res/drawable/ic_vip_lookup_white_48dp.xml deleted file mode 100644 index 0ff876c..0000000 --- a/android/src/main/res/drawable/ic_vip_lookup_white_48dp.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_volume_off_black_24dp.xml b/android/src/main/res/drawable/ic_volume_off_black_24dp.xml deleted file mode 100644 index 7ae7ab9..0000000 --- a/android/src/main/res/drawable/ic_volume_off_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_volume_up_black_24dp.xml b/android/src/main/res/drawable/ic_volume_up_black_24dp.xml deleted file mode 100644 index 0020cdc..0000000 --- a/android/src/main/res/drawable/ic_volume_up_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_vpn_key_black_24dp.xml b/android/src/main/res/drawable/ic_vpn_key_black_24dp.xml deleted file mode 100644 index 3856a04..0000000 --- a/android/src/main/res/drawable/ic_vpn_key_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_webcam_white_48dp.xml b/android/src/main/res/drawable/ic_webcam_white_48dp.xml deleted file mode 100644 index b704747..0000000 --- a/android/src/main/res/drawable/ic_webcam_white_48dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_wifi_black_24dp.xml b/android/src/main/res/drawable/ic_wifi_black_24dp.xml deleted file mode 100644 index 9d07e49..0000000 --- a/android/src/main/res/drawable/ic_wifi_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_wifi_start.xml b/android/src/main/res/drawable/ic_wifi_start.xml deleted file mode 100644 index a5925b7..0000000 --- a/android/src/main/res/drawable/ic_wifi_start.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/monochrome.xml b/android/src/main/res/drawable/monochrome.xml deleted file mode 100644 index 520a273..0000000 --- a/android/src/main/res/drawable/monochrome.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/font/share_tech_mono.ttf b/android/src/main/res/font/share_tech_mono.ttf deleted file mode 100644 index e8d9523..0000000 Binary files a/android/src/main/res/font/share_tech_mono.ttf and /dev/null differ diff --git a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index f6c06b0..0000000 --- a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index d1819e1..0000000 --- a/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/src/main/res/mipmap-hdpi/ic_launcher.png b/android/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 91a5d69..0000000 Binary files a/android/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 4443a63..0000000 Binary files a/android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index f4e7c8c..0000000 Binary files a/android/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/src/main/res/mipmap-mdpi/ic_launcher.png b/android/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index e1e3674..0000000 Binary files a/android/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index e330299..0000000 Binary files a/android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 4310871..0000000 Binary files a/android/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 20be3de..0000000 Binary files a/android/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index 67986d4..0000000 Binary files a/android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index dc27a40..0000000 Binary files a/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 51f8355..0000000 Binary files a/android/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index a2df765..0000000 Binary files a/android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 54c7192..0000000 Binary files a/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 1e07599..0000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 9b6045d..0000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 40e8c6b..0000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/linode_horizontal.webp b/android/src/main/res/mipmap-xxxhdpi/linode_horizontal.webp deleted file mode 100644 index b1fbd5a..0000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/linode_horizontal.webp and /dev/null differ diff --git a/android/src/main/res/mipmap-xxxhdpi/proton_free_horizontal.webp b/android/src/main/res/mipmap-xxxhdpi/proton_free_horizontal.webp deleted file mode 100644 index 209f219..0000000 Binary files a/android/src/main/res/mipmap-xxxhdpi/proton_free_horizontal.webp and /dev/null differ diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml deleted file mode 100644 index 5a6fb95..0000000 --- a/android/src/main/res/values/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #f44336 - \ No newline at end of file diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml deleted file mode 100644 index 4414bc1..0000000 --- a/android/src/main/res/values/strings.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - Linux Command Library - Commands - Basics - Tips - Info - Add bookmark - Remove bookmark - Collapse all - Expand all - Search - Back - Reset - Share - - \ No newline at end of file diff --git a/android/src/test/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsViewModelTest.kt b/android/src/test/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsViewModelTest.kt deleted file mode 100644 index d37a561..0000000 --- a/android/src/test/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/basicgroups/BasicGroupsViewModelTest.kt +++ /dev/null @@ -1,139 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.basicgroups - -import app.cash.turbine.test -import com.linuxcommandlibrary.shared.databaseHelper -import databases.BasicGroup -import io.mockk.every -import io.mockk.mockkObject -import io.mockk.unmockkObject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test - -@OptIn(ExperimentalCoroutinesApi::class) -class BasicGroupsViewModelTest { - - private val testDispatcher = StandardTestDispatcher() - - @Before - fun setUp() { - Dispatchers.setMain(testDispatcher) - mockkObject(databaseHelper) // Assuming databaseHelper is an object - } - - @After - fun tearDown() { - Dispatchers.resetMain() - unmockkObject(databaseHelper) - } - - @Test - fun `init populates basicGroups and initializes empty collapsedMap`() = runTest { - val categoryId = 1L - val mockGroups = listOf( - BasicGroup(1L, "Group 1", categoryId, 0, "icon1"), - BasicGroup(2L, "Group 2", categoryId, 0, "icon2"), - ) - every { databaseHelper.getBasicGroupsByQuery(categoryId) } returns mockGroups - - val viewModel = BasicGroupsViewModel(categoryId) - - viewModel.uiState.test { - val state = awaitItem() - assertEquals(mockGroups.size, state.basicGroups.size) - assertEquals("Group 1", state.basicGroups[0].description) - assertTrue(state.collapsedMap.isEmpty()) - cancelAndIgnoreRemainingEvents() - } - } - - @Test - fun `toggleCollapse updates collapsedMap correctly`() = runTest { - val categoryId = 1L - every { databaseHelper.getBasicGroupsByQuery(categoryId) } returns emptyList() - - val viewModel = BasicGroupsViewModel(categoryId) - val groupId = 123L - - // Initial state - assertFalse(viewModel.isGroupCollapsed(groupId)) - - // Toggle once - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() // Advance time for the update to process - assertTrue(viewModel.uiState.value.collapsedMap.getOrDefault(groupId, false)) - assertTrue(viewModel.isGroupCollapsed(groupId)) - - // Toggle again - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() // Advance time - assertFalse(viewModel.uiState.value.collapsedMap.getOrDefault(groupId, false)) - assertFalse(viewModel.isGroupCollapsed(groupId)) - } - - @Test - fun `toggleCollapse only affects the specified group`() = runTest { - val categoryId = 1L - every { databaseHelper.getBasicGroupsByQuery(categoryId) } returns emptyList() - - val viewModel = BasicGroupsViewModel(categoryId) - val groupId1 = 1L - val groupId2 = 2L - - // Collapse group 1 - viewModel.toggleCollapse(groupId1) - testDispatcher.scheduler.runCurrent() - - assertTrue(viewModel.uiState.value.collapsedMap.getOrDefault(groupId1, false)) - assertFalse(viewModel.uiState.value.collapsedMap.getOrDefault(groupId2, false)) - - // Collapse group 2 - viewModel.toggleCollapse(groupId2) - testDispatcher.scheduler.runCurrent() - - assertTrue(viewModel.uiState.value.collapsedMap.getOrDefault(groupId1, false)) // Should still be true - assertTrue(viewModel.uiState.value.collapsedMap.getOrDefault(groupId2, false)) - } - - @Test - fun `isGroupCollapsed returns correct state`() = runTest { - val categoryId = 1L - every { databaseHelper.getBasicGroupsByQuery(categoryId) } returns emptyList() - val viewModel = BasicGroupsViewModel(categoryId) - val groupId = 1L - - assertFalse(viewModel.isGroupCollapsed(groupId)) // Default - - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() - assertTrue(viewModel.isGroupCollapsed(groupId)) - - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() - assertFalse(viewModel.isGroupCollapsed(groupId)) - } - - @Test - fun `init with empty groups results in empty basicGroups list`() = runTest { - val categoryId = 1L - every { databaseHelper.getBasicGroupsByQuery(categoryId) } returns emptyList() - - val viewModel = BasicGroupsViewModel(categoryId) - - viewModel.uiState.test { - val state = awaitItem() - assertTrue(state.basicGroups.isEmpty()) - assertTrue(state.collapsedMap.isEmpty()) - cancelAndIgnoreRemainingEvents() - } - } -} diff --git a/android/src/test/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchViewModelTest.kt b/android/src/test/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchViewModelTest.kt deleted file mode 100644 index c3d17e1..0000000 --- a/android/src/test/java/com/inspiredandroid/linuxcommandbibliotheca/ui/screens/search/SearchViewModelTest.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.inspiredandroid.linuxcommandbibliotheca.ui.screens.search - -import app.cash.turbine.test -import com.linuxcommandlibrary.shared.databaseHelper -import databases.BasicGroup -import databases.Command -import io.mockk.coEvery -import io.mockk.mockkObject -import io.mockk.unmockkObject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test - -@OptIn(ExperimentalCoroutinesApi::class) -class SearchViewModelTest { - - private val testDispatcher = StandardTestDispatcher() - - @Before - fun setUp() { - Dispatchers.setMain(testDispatcher) - mockkObject(databaseHelper) - } - - @After - fun tearDown() { - Dispatchers.resetMain() - unmockkObject(databaseHelper) - } - - @Test - fun `init creates empty state`() = runTest { - val viewModel = SearchViewModel() - viewModel.uiState.test { - val state = awaitItem() - assertTrue(state.filteredCommands.isEmpty()) - assertTrue(state.filteredBasicGroups.isEmpty()) - assertTrue(state.collapsedMap.isEmpty()) - cancelAndIgnoreRemainingEvents() - } - } - - @Test - fun `search updates filteredCommands and filteredBasicGroups`() = runTest { - val searchText = "test" - val mockCommands = listOf(Command(1L, 0L, "cmd1", "desc1")) - val mockBasicGroups = listOf(BasicGroup(1L, "Group 1", 0L, 0, "icon1")) - - coEvery { databaseHelper.getCommandsByQuery(searchText) } returns mockCommands - coEvery { databaseHelper.getBasicGroupsByQuery(searchText) } returns mockBasicGroups - - val viewModel = SearchViewModel() - viewModel.search(searchText) - testDispatcher.scheduler.advanceUntilIdle() // Ensure coroutines complete - - viewModel.uiState.test { - val state = expectMostRecentItem() // Get the latest state - assertEquals(1, state.filteredCommands.size) - assertEquals("cmd1", state.filteredCommands[0].name) - assertEquals(1, state.filteredBasicGroups.size) - assertEquals("Group 1", state.filteredBasicGroups[0].description) - assertTrue(state.collapsedMap.isEmpty()) // Should not change - } - } - - @Test - fun `search with blank text clears results`() = runTest { - // Populate with some initial data - val initialSearchText = "test" - val mockCommands = listOf(Command(1L, 0L, "cmd1", "desc1")) - val mockBasicGroups = listOf(BasicGroup(1L, "Group 1", 0L, 0, "icon1")) - coEvery { databaseHelper.getCommandsByQuery(initialSearchText) } returns mockCommands - coEvery { databaseHelper.getBasicGroupsByQuery(initialSearchText) } returns mockBasicGroups - - val viewModel = SearchViewModel() - viewModel.search(initialSearchText) - testDispatcher.scheduler.advanceUntilIdle() - - // Ensure initial data is there - var latestState = viewModel.uiState.value - assertFalse(latestState.filteredCommands.isEmpty()) - assertFalse(latestState.filteredBasicGroups.isEmpty()) - - // Search with blank text - viewModel.search(" ") // Blank text - testDispatcher.scheduler.advanceUntilIdle() - - latestState = viewModel.uiState.value - assertTrue(latestState.filteredCommands.isEmpty()) - assertTrue(latestState.filteredBasicGroups.isEmpty()) - } - - @Test - fun `search cancels previous search`() = runTest { - val searchText1 = "search1" - val searchText2 = "search2" - - coEvery { databaseHelper.getCommandsByQuery(searchText1) } coAnswers { - delay(500) // Simulate long running search - listOf(Command(1L, 0L, "cmd1_from_search1", "desc1")) - } - coEvery { databaseHelper.getBasicGroupsByQuery(searchText1) } returns emptyList() - - coEvery { databaseHelper.getCommandsByQuery(searchText2) } returns listOf(Command(2L, 0L, "cmd2_from_search2", "desc2")) - coEvery { databaseHelper.getBasicGroupsByQuery(searchText2) } returns emptyList() - - val viewModel = SearchViewModel() - - viewModel.search(searchText1) - testDispatcher.scheduler.advanceTimeBy(100) // Start search1 but don't let it finish - - viewModel.search(searchText2) // This should cancel search1 - testDispatcher.scheduler.advanceUntilIdle() // Let search2 complete - - val state = viewModel.uiState.value - assertEquals(1, state.filteredCommands.size) - assertEquals("cmd2_from_search2", state.filteredCommands[0].name) // Only results from search2 - } - - @Test - fun `toggleCollapse updates collapsedMap correctly`() = runTest { - val viewModel = SearchViewModel() - val groupId = 123L - - assertFalse(viewModel.isGroupCollapsed(groupId)) - - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() - assertTrue(viewModel.uiState.value.collapsedMap.getOrDefault(groupId, false)) - assertTrue(viewModel.isGroupCollapsed(groupId)) - - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() - assertFalse(viewModel.uiState.value.collapsedMap.getOrDefault(groupId, false)) - assertFalse(viewModel.isGroupCollapsed(groupId)) - - // Ensure search results are not affected - assertTrue(viewModel.uiState.value.filteredCommands.isEmpty()) - assertTrue(viewModel.uiState.value.filteredBasicGroups.isEmpty()) - } - - @Test - fun `isGroupCollapsed returns correct state`() = runTest { - val viewModel = SearchViewModel() - val groupId = 1L - - assertFalse(viewModel.isGroupCollapsed(groupId)) // Default - - viewModel.toggleCollapse(groupId) - testDispatcher.scheduler.runCurrent() - assertTrue(viewModel.isGroupCollapsed(groupId)) - } -} diff --git a/art/assetlinks.json b/art/assetlinks.json deleted file mode 100644 index 1e5b4f7..0000000 --- a/art/assetlinks.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "relation": [ - "delegate_permission/common.handle_all_urls" - ], - "target": { - "namespace": "android_app", - "package_name": "com.inspiredandroid.linuxcommandbibliotheca", - "sha256_cert_fingerprints": [ - "01:76:7F:F9:25:17:A7:25:AF:AE:87:3F:40:E3:67:BD:A4:2F:0E:DB:A5:65:3A:D1:5C:AF:04:93:C6:43:5E:60" - ] - } - } -] \ No newline at end of file diff --git a/art/fdroid_badge.png b/art/fdroid_badge.png deleted file mode 100644 index 60521a3..0000000 Binary files a/art/fdroid_badge.png and /dev/null differ diff --git a/art/feature.png b/art/feature.png deleted file mode 100644 index 31ba40b..0000000 Binary files a/art/feature.png and /dev/null differ diff --git a/art/funktions.png b/art/funktions.png deleted file mode 100644 index 70d0109..0000000 Binary files a/art/funktions.png and /dev/null differ diff --git a/art/funktions.xcf b/art/funktions.xcf deleted file mode 100644 index e8611f3..0000000 Binary files a/art/funktions.xcf and /dev/null differ diff --git a/art/funktions_1024x576.png b/art/funktions_1024x576.png deleted file mode 100644 index 30d2038..0000000 Binary files a/art/funktions_1024x576.png and /dev/null differ diff --git a/art/funktions_huawei_1.png b/art/funktions_huawei_1.png deleted file mode 100644 index 2d537f8..0000000 Binary files a/art/funktions_huawei_1.png and /dev/null differ diff --git a/art/funktions_huawei_2.png b/art/funktions_huawei_2.png deleted file mode 100644 index 6be8e7b..0000000 Binary files a/art/funktions_huawei_2.png and /dev/null differ diff --git a/art/funktions_huawei_3.png b/art/funktions_huawei_3.png deleted file mode 100644 index c3a8a64..0000000 Binary files a/art/funktions_huawei_3.png and /dev/null differ diff --git a/art/funktions_huawei_4.png b/art/funktions_huawei_4.png deleted file mode 100644 index 47fe364..0000000 Binary files a/art/funktions_huawei_4.png and /dev/null differ diff --git a/art/funktions_huawei_5.jpg b/art/funktions_huawei_5.jpg deleted file mode 100644 index 5007eab..0000000 Binary files a/art/funktions_huawei_5.jpg and /dev/null differ diff --git a/art/funktions_huawei_5.png b/art/funktions_huawei_5.png deleted file mode 100644 index beb943c..0000000 Binary files a/art/funktions_huawei_5.png and /dev/null differ diff --git a/art/ios_1024.png b/art/ios_1024.png deleted file mode 100644 index 1f2e6f6..0000000 Binary files a/art/ios_1024.png and /dev/null differ diff --git a/art/ios_512.png b/art/ios_512.png deleted file mode 100644 index fab209a..0000000 Binary files a/art/ios_512.png and /dev/null differ diff --git a/art/play_store_badge.png b/art/play_store_badge.png deleted file mode 100644 index 1b10bd6..0000000 Binary files a/art/play_store_badge.png and /dev/null differ diff --git a/art/screen-1-dark.png b/art/screen-1-dark.png deleted file mode 100644 index 741ff58..0000000 Binary files a/art/screen-1-dark.png and /dev/null differ diff --git a/art/screen-1-tablet-dark.png b/art/screen-1-tablet-dark.png deleted file mode 100644 index 8e8b040..0000000 Binary files a/art/screen-1-tablet-dark.png and /dev/null differ diff --git a/art/screen-1-tablet.png b/art/screen-1-tablet.png deleted file mode 100644 index 849ca35..0000000 Binary files a/art/screen-1-tablet.png and /dev/null differ diff --git a/art/screen-1.png b/art/screen-1.png deleted file mode 100644 index 77edb6e..0000000 Binary files a/art/screen-1.png and /dev/null differ diff --git a/art/screen-2-dark.png b/art/screen-2-dark.png deleted file mode 100644 index 56a16be..0000000 Binary files a/art/screen-2-dark.png and /dev/null differ diff --git a/art/screen-2-tablet-dark.png b/art/screen-2-tablet-dark.png deleted file mode 100644 index 0ddf464..0000000 Binary files a/art/screen-2-tablet-dark.png and /dev/null differ diff --git a/art/screen-2-tablet.png b/art/screen-2-tablet.png deleted file mode 100644 index e7c2357..0000000 Binary files a/art/screen-2-tablet.png and /dev/null differ diff --git a/art/screen-2.png b/art/screen-2.png deleted file mode 100644 index 95ee774..0000000 Binary files a/art/screen-2.png and /dev/null differ diff --git a/art/screen-3-dark.png b/art/screen-3-dark.png deleted file mode 100644 index 854a966..0000000 Binary files a/art/screen-3-dark.png and /dev/null differ diff --git a/art/screen-3.png b/art/screen-3.png deleted file mode 100644 index b312557..0000000 Binary files a/art/screen-3.png and /dev/null differ diff --git a/art/screen-4-dark.png b/art/screen-4-dark.png deleted file mode 100644 index cde4e06..0000000 Binary files a/art/screen-4-dark.png and /dev/null differ diff --git a/art/screen-4.png b/art/screen-4.png deleted file mode 100644 index c80559e..0000000 Binary files a/art/screen-4.png and /dev/null differ diff --git a/art/screen-cli-1.png b/art/screen-cli-1.png deleted file mode 100644 index 4e8b49f..0000000 Binary files a/art/screen-cli-1.png and /dev/null differ diff --git a/art/tux.xcf b/art/tux.xcf deleted file mode 100644 index 6de4fe0..0000000 Binary files a/art/tux.xcf and /dev/null differ diff --git a/art/web_badge.png b/art/web_badge.png deleted file mode 100644 index 426bccc..0000000 Binary files a/art/web_badge.png and /dev/null differ diff --git a/art/web_hi_res_144.png b/art/web_hi_res_144.png deleted file mode 100644 index 7fa61a2..0000000 Binary files a/art/web_hi_res_144.png and /dev/null differ diff --git a/art/web_hi_res_512.png b/art/web_hi_res_512.png deleted file mode 100644 index c88c437..0000000 Binary files a/art/web_hi_res_512.png and /dev/null differ diff --git a/art/web_hi_res_512.xcf b/art/web_hi_res_512.xcf deleted file mode 100644 index 5238d4e..0000000 Binary files a/art/web_hi_res_512.xcf and /dev/null differ diff --git a/art/web_hi_res_512_backgroundless.png b/art/web_hi_res_512_backgroundless.png deleted file mode 100644 index 4574101..0000000 Binary files a/art/web_hi_res_512_backgroundless.png and /dev/null differ diff --git a/assets/database.db b/assets/database.db deleted file mode 100644 index 680852b..0000000 Binary files a/assets/database.db and /dev/null differ diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 3909b1f..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,46 +0,0 @@ -import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask - -plugins { - alias(libs.plugins.ben.manes.versions) - alias(libs.plugins.spotless) - alias(libs.plugins.kotlin.multiplatform) apply false - alias(libs.plugins.kotlin.android) apply false - alias(libs.plugins.android.application) apply false - alias(libs.plugins.android.library) apply false - alias(libs.plugins.sqldelight) apply false - alias(libs.plugins.compose.compiler) apply false -} - -group = "com.inspiredandroid" - -tasks.withType { - rejectVersionIf { - isNonStable(candidate.version) - } -} - -configure { - kotlin { - target("**/*.kt") - ktlint() - .editorConfigOverride( - mapOf( - "ktlint_standard_no-wildcard-imports" to "disabled", - "ktlint_standard_package-name" to "disabled", - "ktlint_standard_function-naming" to "disabled", - "ktlint_standard_discouraged-comment-location" to "disabled", - ), - ) - } - kotlinGradle { - target("**/*.gradle.kts") - ktlint() - } -} - -fun isNonStable(version: String): Boolean { - val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) } - val regex = "^[0-9,.v-]+(-r)?$".toRegex() - val isStable = stableKeyword || regex.matches(version) - return isStable.not() -} diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts deleted file mode 100644 index a01076c..0000000 --- a/cli/build.gradle.kts +++ /dev/null @@ -1,34 +0,0 @@ -plugins { - kotlin("jvm") -} - -group = "com.linuxcommandlibrary" -version = parent!!.version - -dependencies { - implementation(project(":common")) - implementation(libs.sqldelight.sqlite.driver) -} - -kotlin { - compilerOptions { - sourceSets["main"].apply { - resources.srcDirs("../assets") - } - } -} - -val createJar = - tasks.register("createJar", Jar::class) { - archiveBaseName.set("MyApplication") - from(sourceSets["main"].output) - - archiveFileName.set("linuxcommandlibrary.jar") - manifest { - attributes["Main-Class"] = "com.linuxcommandlibrary.cli.ConsoleApplicationKt" - } - from(configurations.getByName("runtimeClasspath").map { if (it.isDirectory) it else zipTree(it) }) - - duplicatesStrategy = DuplicatesStrategy.INCLUDE - exclude("META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA") - } diff --git a/cli/src/main/kotlin/com/linuxcommandlibrary/cli/ConsoleApplication.kt b/cli/src/main/kotlin/com/linuxcommandlibrary/cli/ConsoleApplication.kt deleted file mode 100644 index 1eaf5cd..0000000 --- a/cli/src/main/kotlin/com/linuxcommandlibrary/cli/ConsoleApplication.kt +++ /dev/null @@ -1,187 +0,0 @@ -package com.linuxcommandlibrary.cli - -import com.linuxcommandlibrary.shared.Version -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.initDatabase -import com.linuxcommandlibrary.shared.sortedSearch -import kotlin.system.exitProcess - -const val BOLD = "\u001b[1m" -const val RESET = "\u001b[0m" - -fun main() { - initDatabase() - - showIntro() - - showStartMenu() -} - -fun showIntro() { - println(" _ _ __ _ __ __ __ __") - println("| |__ | || \\| || | |\\ \\/ /") - println("|____||_||_|\\__| \\___/ /_/\\_\\") - println(" ____ ____ __ __ __ __ ____ __ _ ____") - println("/ (__`/ () \\| \\/ || \\/ | / () \\ | \\| || _) \\") - println("\\____)\\____/|_|\\/|_||_|\\/|_|/__/\\__\\|_|\\__||____/") - println(" _ _ _____ _____ ____ _____ __ __") - println("| |__ | || () )| () ) / () \\ | () )\\ \\/ /") - println("|____||_||_()_)|_|\\_\\/__/\\__\\|_|\\_\\ |__|") - println("Version: ${Version.appVersion}") -} - -fun showStartMenu() { - println() - println("1 Commands") - println("2 Basics") - println("3 Tips") - println() - println("0 Exit") - - when (readNumber()) { - 0 -> exitProcess(0) - 1 -> showSearch() - 2 -> showBasicCategories() - 3 -> showTips() - else -> { - println("Invalid input") - showStartMenu() - } - } -} - -fun showSearch() { - print("Search: ") - val input = readlnOrNull() ?: "" - val commands = databaseHelper.getCommandsByQuery(input).sortedSearch(input).take(10) - if (commands.isEmpty()) { - println("No results for \"$input\"") - showSearch() - return - } - commands.forEachIndexed { index, command -> - println("${index + 1} ${command.name}") - } - println() - println("0 Back") - - when (val choice = readNumber()) { - 0 -> showStartMenu() - in 1..commands.size -> { - val name = commands[choice - 1].name - showCommand(name) - } - - else -> { - println("Invalid input") - showSearch() - } - } -} - -fun showCommand(name: String) { - val commandId = databaseHelper.getCommand(name)?.id ?: return - - databaseHelper.getSections(commandId).forEach { - println(BOLD + it.title + RESET) - - println( - it.content.replace("
", "\n").replace("", BOLD).replace("", RESET) - .replace(Regex("s/<(.*?)>//g"), "").replace(" ", "").replace("&", ""), - ) - println() - } - - println("Press enter") - readlnOrNull() - showStartMenu() -} - -fun showBasicCategories() { - val categories = databaseHelper.getBasics() - categories.forEachIndexed { index, basicCategory -> - println("${index + 1} " + basicCategory.title) - } - println() - println("0 Back") - - when (val choice = readNumber()) { - 0 -> showStartMenu() - in 1..categories.size -> { - val id = categories[choice - 1].id - showBasicGroups(id) - } - - else -> { - println("Invalid input") - showBasicCategories() - } - } -} - -fun showBasicGroups(id: Long) { - databaseHelper.getBasicGroupsByQuery(id).forEach { group -> - println("$BOLD${group.description}$RESET") - databaseHelper.getBasicCommands(group.id).forEach { command -> - println("- " + command.command) - } - println() - } - - println("Press enter") - readlnOrNull() - showBasicCategories() -} - -fun showTips() { - val tips = databaseHelper.getTips() - tips.forEachIndexed { index, tip -> - println("${index + 1} ${tip.title}") - } - println() - println("0 Back") - - when (val choice = readNumber()) { - 0 -> showStartMenu() - in 1..tips.size -> { - val id = tips[choice - 1].id - showTipsDetail(id) - } - - else -> { - println("Invalid input") - showTips() - } - } -} - -fun showTipsDetail(id: Long) { - databaseHelper.getTipSections().filter { it.tip_id == id }.forEach { - when (it.type) { - 0L -> { - printTipData(it.data1) - } - - 1L -> { - printTipData(it.data1) - } - - 3L -> { - if (it.data1.isNotBlank()) { - printTipData(it.data1) - } - printTipData(it.data2) - } - } - } - - println("Press enter") - readlnOrNull() - showTips() -} - -fun printTipData(data: String) { - println(data.replace("\\n", "").replace("", BOLD).replace("", RESET)) -} - -fun readNumber(): Int = readlnOrNull()?.toIntOrNull() ?: -1 diff --git a/common/build.gradle.kts b/common/build.gradle.kts deleted file mode 100644 index 0552432..0000000 --- a/common/build.gradle.kts +++ /dev/null @@ -1,94 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.kotlin.multiplatform) - alias(libs.plugins.android.library) - alias(libs.plugins.sqldelight) -} - -group = "com.linuxcommandlibrary" - -kotlin { - androidTarget() - jvm() - - androidTarget { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) - } - } - - sourceSets { - commonMain { - dependencies { - implementation(libs.runtime) - } - kotlin.srcDir(layout.buildDirectory.dir("generated/src/commonMain/kotlin")) - } - commonTest { - dependencies { - implementation(kotlin("test")) - } - } - androidMain { - dependencies { - implementation(libs.sqldelight.android.driver) - } - } - jvmMain { - dependencies { - implementation(libs.sqldelight.sqlite.driver) - } - } - } -} - -android { - compileSdk = 35 - sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") - defaultConfig { - minSdk = 24 - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - - lint { - abortOnError = false - } - namespace = "com.linuxcommandlibrary.shared" -} - -sqldelight { - databases { - create("CommandDatabase") { - packageName.set("com.linuxcommandlibrary") - } - } -} - -class VersionGeneratorPlugin : Plugin { - override fun apply(project: Project) { - project.afterEvaluate { - val versionFile = - layout.buildDirectory - .file("generated/src/commonMain/kotlin/com/linuxcommandlibrary/shared/Version.kt") - .get() - .asFile - versionFile.parentFile?.mkdirs() - versionFile.writeText( - """ - package com.linuxcommandlibrary.shared - - object Version { - const val appVersion = "${libs.versions.appVersion.get()}" - } - """.trimIndent(), - ) - } - } -} - -apply() diff --git a/common/src/androidMain/AndroidManifest.xml b/common/src/androidMain/AndroidManifest.xml deleted file mode 100644 index 568741e..0000000 --- a/common/src/androidMain/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/common/src/androidMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt b/common/src/androidMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt deleted file mode 100644 index a01a42f..0000000 --- a/common/src/androidMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.linuxcommandlibrary.shared - -import android.content.Context -import app.cash.sqldelight.db.SqlDriver -import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import com.linuxcommandlibrary.CommandDatabase -import java.io.File -import java.io.InputStream -import java.io.OutputStream - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -actual var databaseHelper = DatabaseHelper() - -fun hasDatabase(context: Context): Boolean { - val file = File(context.dataDir, "databases/database.db") - return file.exists() -} - -fun copyDatabase(context: Context, onUpdateStatus: (Int) -> Unit = {}) { - val databaseFolder = File(context.dataDir, "databases") - if (!databaseFolder.exists()) { - databaseFolder.mkdir() - } - val file = File(databaseFolder, "database.db") - - val inputStream = context.assets.open("database.db") - inputStream.copyToWithStatus(file.outputStream(), onUpdateStatus) - inputStream.close() - - // Delete old realm database - val filesFolder = File(context.dataDir, "files") - if (filesFolder.exists()) { - File(filesFolder, "database.realm").delete() - } -} - -fun InputStream.copyToWithStatus(out: OutputStream, onUpdateStatus: (Int) -> Unit = {}): Long { - var bytesCopied: Long = 0 - val buffer = ByteArray(DEFAULT_BUFFER_SIZE) - var bytes = read(buffer) - val totalSize = this.available().toFloat() - while (bytes >= 0) { - out.write(buffer, 0, bytes) - bytesCopied += bytes - bytes = read(buffer) - onUpdateStatus(bytesCopied.div(totalSize).times(100f).toInt()) - } - return bytesCopied -} - -fun initDatabase(context: Context) { - val driver: SqlDriver = AndroidSqliteDriver(CommandDatabase.Schema, context, "database.db") - databaseHelper.setupDriver(driver) -} diff --git a/common/src/cliMain/kotlin/com/linuxcommandlibrary/shared/DesktopApp.kt b/common/src/cliMain/kotlin/com/linuxcommandlibrary/shared/DesktopApp.kt deleted file mode 100644 index 2b62b8c..0000000 --- a/common/src/cliMain/kotlin/com/linuxcommandlibrary/shared/DesktopApp.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.linuxcommandlibrary.shared - -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.runtime.Composable - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -@Preview -@Composable -fun AppPreview() { - // App() -} diff --git a/common/src/cliMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt b/common/src/cliMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt deleted file mode 100644 index 408310f..0000000 --- a/common/src/cliMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.linuxcommandlibrary.shared - -import com.linuxcommandlibrary.CommandDatabase -import com.squareup.sqldelight.db.SqlDriver -import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver -import java.io.File - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -actual fun getPlatformName(): String = "Desktop" - -actual var databaseHelper = DatabaseHelper() - -fun initDatabase() { - val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:assets/database.db") - if (!File("assets/database.db").exists()) { - CommandDatabase.Schema.create(driver) - } - databaseHelper.setupDriver(driver) -} diff --git a/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/App.kt b/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/App.kt deleted file mode 100644 index 7b98f1e..0000000 --- a/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/App.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.linuxcommandlibrary.shared - -import databases.BasicCategory -import databases.Command -import databases.CommandSection -import java.util.Locale - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -sealed class CommandElement { - data class Text(val text: String) : CommandElement() - data class Man(val man: String) : CommandElement() - data class Url(val command: String, val url: String) : CommandElement() -} - -/** - * Search in name and description and return sorted by priority - */ -fun List.sortedSearch(phrase: String): List = this.sortedBy { - val name = it.name.lowercase() - val lowercasePhrase = phrase.lowercase() - when { - !name.contains(lowercasePhrase) -> 30 - name == lowercasePhrase -> 0 - name.startsWith(lowercasePhrase) -> 10 - else -> 20 - } -} - -/** - * Return a list of sealed Elements for visual representation - */ -fun String.getCommandList( - mans: String, - hasBrackets: Boolean = false, - checkExisting: Boolean = false, -): List { - var command = " $this" - val list = mutableListOf() - mans.split(",").filterNot { it.isEmpty() }.map { it.replace("(", "").replace(")", "") } - .forEach { - command = if (it.startsWith("url:")) { - val cmd = it.substring(4).split("|").first() - command.replace(cmd, " ü${it}ä") - } else { - if (hasBrackets) { - val escapedIt = Regex.escape(it) // Escapes special characters, e.g., "pbmto\\*\\*\\*" - val regex = "(?:[\\s,])($escapedIt)".toRegex() - command.replace(regex, " ü${it}ä") - } else { - command.replace(it, " ü${it}ä") - } - } - } - - var currentText = "" - var currentCommand = "" - var isCommand = false - command.trim().forEach { - if (it == 'ü') { - list.add(CommandElement.Text(currentText.replace("\n", ""))) - currentText = "" - isCommand = true - } else if (it == 'ä') { - if (currentCommand.isNotBlank()) { - when { - currentCommand.startsWith("url:") -> { - val url = currentCommand.split("|").last() - val cmd = currentCommand.substring(4).split("|").first() - list.add(CommandElement.Url(cmd, url)) - } - - checkExisting && databaseHelper.getCommand(currentCommand) == null -> { - list.add(CommandElement.Text(currentCommand)) - } - - else -> { - list.add(CommandElement.Man(currentCommand)) - } - } - } - currentCommand = "" - isCommand = false - } else { - if (isCommand) { - currentCommand += it - } else { - currentText += it - } - } - } - list.add(CommandElement.Text(currentText.replace("[cmd]", "[command]").replace("\n", ""))) - return list.toList() -} - -val onlyCharactersRegex = "[^a-z]".toRegex() - -/** - * Only allow characters in html file names to guarantee matching on the website and app deep linking - */ -fun BasicCategory.getHtmlFileName(): String = this.title.lowercase(Locale.US).replace(onlyCharactersRegex, "") - -/** - * Show TLDR and SYNOPSIS always on the top and SEE ALSO and AUTHOR on the bottom. Everything else in between - */ -fun CommandSection.getSortPriority(): Int = when (this.title) { - "TLDR" -> 0 - "SYNOPSIS" -> 10 - "SEE ALSO" -> 90 - "AUTHOR" -> 100 - else -> 50 -} - -fun String.isLetter(): Boolean = this.firstOrNull() in 'a'..'z' || this.firstOrNull() in 'A'..'Z' diff --git a/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/EmptyClass.kt b/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/EmptyClass.kt deleted file mode 100644 index 3d621fd..0000000 --- a/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/EmptyClass.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.linuxcommandlibrary.shared - -class EmptyClass diff --git a/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt b/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt deleted file mode 100644 index ab5f03b..0000000 --- a/common/src/commonMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.linuxcommandlibrary.shared - -import app.cash.sqldelight.db.SqlDriver -import com.linuxcommandlibrary.CommandDatabase -import databases.BasicCategory -import databases.BasicCommand -import databases.BasicGroup -import databases.Command -import databases.CommandQueries -import databases.CommandSection -import databases.Tip -import databases.TipSection - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -expect var databaseHelper: DatabaseHelper - -class DatabaseHelper { - - private lateinit var sqlDriver: SqlDriver - private lateinit var commandQueries: CommandQueries - - fun setupDriver(driver: SqlDriver) { - sqlDriver = driver - // println("Setup driver: $sqlDriver") - commandQueries = CommandDatabase(sqlDriver).commandQueries - } - - fun getCommand(name: String): Command? = commandQueries.selectCommandByName(name).executeAsOneOrNull() - - fun getCommands(): List = commandQueries.selectCommands().executeAsList().sortedBy { !it.name.isLetter() } - - fun getCommandsByQuery(query: String): List = commandQueries.selectCommandsByQuery(query, query).executeAsList() - - fun getBasics(): List = commandQueries.selectBasicCategories().executeAsList() - - fun getBasicGroupsByQuery(categoryId: Long): List = commandQueries.selectBasicGroupByCategory(categoryId).executeAsList() - - fun getBasicCommands(groupId: Long): List = commandQueries.selectBasicCommandByGroupId(groupId).executeAsList() - - fun getBasicGroupsByQuery(query: String): List = commandQueries.selectBasicGroupsByQuery(query).executeAsList() - - fun getSections(commandId: Long): List = commandQueries.selectCommandSectionsByCommandId(commandId).executeAsList() - - fun getTips(): List = commandQueries.selectTips().executeAsList() - - fun getTipSections(): List = commandQueries.selectAllTipSections().executeAsList() -} diff --git a/common/src/commonMain/sqldelight/databases/Command.sq b/common/src/commonMain/sqldelight/databases/Command.sq deleted file mode 100644 index 62901f4..0000000 --- a/common/src/commonMain/sqldelight/databases/Command.sq +++ /dev/null @@ -1,141 +0,0 @@ -CREATE TABLE IF NOT EXISTS BasicGroup ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "position" INTEGER NOT NULL, - "description" TEXT NOT NULL, - "category_id" INTEGER NOT NULL -); - -CREATE TABLE IF NOT EXISTS BasicCommand ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "command" TEXT NOT NULL, - "mans" TEXT NOT NULL, - "group_id" INTEGER NOT NULL -); - -CREATE TABLE IF NOT EXISTS BasicCategory ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "position" INTEGER NOT NULL, - "title" TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS Command ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "category" INTEGER NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS CommandSection ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "title" TEXT NOT NULL, - "content" TEXT NOT NULL, - "command_id" INTEGER NOT NULL -); - -CREATE TABLE IF NOT EXISTS Tip ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "title" TEXT NOT NULL, - "position" INTEGER NOT NULL -); - -CREATE TABLE IF NOT EXISTS TipSection ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "tip_id" INTEGER NOT NULL, - "position" INTEGER NOT NULL, - "type" INTEGER NOT NULL, - "data1" TEXT NOT NULL, - "data2" TEXT NOT NULL, - "extra" TEXT NOT NULL -); - -selectBasicCategories: -SELECT * -FROM BasicCategory -ORDER BY position; - -selectBasicGroupByCategory: -SELECT * -FROM BasicGroup -WHERE category_id = ? -ORDER BY position DESC; - -selectBasicGroupsByQuery: -SELECT * -FROM BasicGroup -WHERE UPPER(description) LIKE '%' || UPPER(?) || '%' -AND category_id != 250 -AND category_id != 253 -AND category_id != 254 -AND category_id != 255 -AND category_id != 256 -AND category_id != 260 -ORDER BY position DESC; - -selectBasicCommandByGroupId: -SELECT * -FROM BasicCommand -WHERE group_id = ?; - -insertCommand: -INSERT INTO Command (category, name, description) -VALUES (?, ?, ?); - -insertBasicGroup: -INSERT INTO BasicGroup (position, description, category_id) -VALUES (?, ?, ?); - -insertBasicCommand: -INSERT INTO BasicCommand (command, mans, group_id) -VALUES (?, ?, ?); - -selectCommands: -SELECT * -FROM Command -ORDER BY name COLLATE NOCASE ASC; - -selectCommandsByQuery: -SELECT * -FROM Command -WHERE UPPER(name) LIKE '%' || UPPER(?) || '%' - OR UPPER(description) LIKE '%' || UPPER(?) || '%' -ORDER BY name COLLATE NOCASE ASC; - -selectCommandByName: -SELECT * -FROM Command -WHERE name = ?; - -deleteCommandSections: -DELETE FROM CommandSection -WHERE command_id = ? AND title != "TLDR"; - -insertCommandSection: -INSERT INTO CommandSection (title, content, command_id) -VALUES (?, ?, ?); - -selectCommandSectionsByCommandId: -SELECT * -FROM CommandSection -WHERE command_id = ? AND title != "NAME" -ORDER BY id; - -updateCommandTLDRSectionByCommandId: -UPDATE CommandSection -SET content = ? -WHERE command_id = ? AND title = "TLDR"; - -selectTips: -SELECT * -FROM Tip -ORDER BY position; - -selectTipSections: -SELECT * -FROM TipSection -WHERE tip_id = ? -ORDER BY position; - -selectAllTipSections: -SELECT * -FROM TipSection -ORDER BY position; \ No newline at end of file diff --git a/common/src/commonTest/kotlin/CommonTests.kt b/common/src/commonTest/kotlin/CommonTests.kt deleted file mode 100644 index 6498392..0000000 --- a/common/src/commonTest/kotlin/CommonTests.kt +++ /dev/null @@ -1,63 +0,0 @@ -import com.linuxcommandlibrary.shared.CommandElement -import com.linuxcommandlibrary.shared.getCommandList -import com.linuxcommandlibrary.shared.getHtmlFileName -import com.linuxcommandlibrary.shared.getSortPriority -import com.linuxcommandlibrary.shared.sortedSearch -import databases.BasicCategory -import databases.Command -import databases.CommandSection -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class CommonTests { - - @Test - fun testCommandListElements() { - val command = "ps ax | grep firefox" - val elements = command.getCommandList("ps,grep") - assertTrue(elements.count { it is CommandElement.Man } == 2) - } - - @Test - fun testCommandListSearch() { - val commands = listOf( - Command(0, 0, "optipng", "convert"), - Command(0, 0, "thumbnail", "take png and do something"), - Command(0, 0, "Pngcheck", "print detailed"), - Command(0, 0, "png", "png"), - ) - - val filteredCommands = commands.sortedSearch("png") - - assert(filteredCommands.size == 4) - - assertEquals(filteredCommands[0].name, "png") - assertEquals(filteredCommands[1].name, "Pngcheck") - assertEquals(filteredCommands[2].name, "optipng") - assertEquals(filteredCommands[3].name, "thumbnail") - } - - @Test - fun testBasicCategory() { - val category = BasicCategory(0L, 0L, "Users & Groups 2") - assertEquals(category.getHtmlFileName(), "usersgroups") - } - - @Test - fun testSectionSorting() { - val sections = listOf( - CommandSection(0L, "SEE ALSO", "", 0L), - CommandSection(0L, "RANDOM", "", 0L), - CommandSection(0L, "TLDR", "", 0L), - CommandSection(0L, "AUTHOR", "", 0L), - CommandSection(0L, "SYNOPSIS", "", 0L), - ).sortedBy { it.getSortPriority() } - - assertEquals("TLDR", sections[0].title) - assertEquals("SYNOPSIS", sections[1].title) - assertEquals("RANDOM", sections[2].title) - assertEquals("SEE ALSO", sections[3].title) - assertEquals("AUTHOR", sections[4].title) - } -} diff --git a/common/src/jvmMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt b/common/src/jvmMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt deleted file mode 100644 index 932fd11..0000000 --- a/common/src/jvmMain/kotlin/com/linuxcommandlibrary/shared/Platform.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.linuxcommandlibrary.shared - -import app.cash.sqldelight.db.SqlDriver -import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver -import com.linuxcommandlibrary.CommandDatabase -import java.io.File - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -actual var databaseHelper = DatabaseHelper() - -fun initDatabase() { - val databaseFile = EmptyClass::class.java.classLoader?.getResource("database.db")?.toURI() - val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite::resource:$databaseFile") - if (!File("assets/database.db").exists()) { - CommandDatabase.Schema.create(driver) - } - databaseHelper.setupDriver(driver) -} diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts deleted file mode 100644 index 267df86..0000000 --- a/desktop/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - kotlin("jvm") -} - -group = "com.linuxcommandlibrary" -version = "1.0" - -dependencies { - implementation(project(":common")) - implementation(libs.kotlinx.html.jvm) - implementation(libs.json) - implementation(libs.sqldelight.sqlite.driver) - implementation(libs.kotlinx.coroutines.core) -} - -kotlin { - compilerOptions { - sourceSets["main"].apply { - resources.srcDirs("../assets") - } - } -} diff --git a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/FdroiInfoBuilder.kt b/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/FdroiInfoBuilder.kt deleted file mode 100644 index ed6d6bf..0000000 --- a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/FdroiInfoBuilder.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.linuxcommandlibrary.desktop - -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.initDatabase -import java.io.File -import java.io.PrintStream - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -fun main() { - initDatabase() - - val builder = FdroidInfoBuilder() - builder.buildFullDescription() - builder.buildShortDescription() -} - -class FdroidInfoBuilder { - - fun buildFullDescription() { - val file = File("fastlane/metadata/android/en-US/full_description.txt") - val stream = PrintStream(file) - stream.appendLine("The app currently has ${databaseHelper.getCommands().size} manual pages, ${databaseHelper.getBasics().size} basic categories and a bunch of general terminal tips. It works 100% offline, doesn't need an internet connection and has no tracking software.") - stream.appendLine() - stream.appendLine("Categories") - stream.appendLine() - databaseHelper.getBasics().forEach { category -> - stream.appendLine("* ${category.title}") - } - stream.appendLine() - stream.appendLine("Tips") - stream.appendLine() - databaseHelper.getTips().forEach { tip -> - stream.appendLine("* ${tip.title}") - } - - stream.close() - } - - fun buildShortDescription() { - val file = File("fastlane/metadata/android/en-US/short_description.txt") - val stream = PrintStream(file) - stream.appendLine("${databaseHelper.getCommands().size} manual pages, ${databaseHelper.getBasics().size} basic categories and a bunch of general terminal tips.") - - stream.close() - } -} diff --git a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/MarkdownBuilder.kt b/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/MarkdownBuilder.kt deleted file mode 100644 index 7a4d9e0..0000000 --- a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/MarkdownBuilder.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.linuxcommandlibrary.desktop - -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.initDatabase -import java.io.File -import java.io.PrintStream - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -fun main() { - initDatabase() - - val markdownBuilder = MarkdownBuilder() - markdownBuilder.build() -} - -class MarkdownBuilder { - fun build() { - val file = File("README.md") - val stream = PrintStream(file) - stream.appendLine("## Linux Command Library (Mobile+CLI+Web)") - stream.appendLine() - stream.appendLine("![Icon](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandLibrary/master/art/web_hi_res_144.png)") - stream.appendLine() - stream.appendLine("The app currently has **${databaseHelper.getCommands().size}** manual pages, **${databaseHelper.getBasics().size}+** basic categories and a bunch of general terminal tips. It works 100% offline, doesn't need an internet connection and has no tracking software.") - stream.appendLine() - stream.appendLine("[![Play Store](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandBibliotheca/master/art/play_store_badge.png)](https://play.google.com/store/apps/details?id=com.inspiredandroid.linuxcommandbibliotheca)") - stream.appendLine("[![F-Droid](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandBibliotheca/master/art/fdroid_badge.png)](https://f-droid.org/en/packages/com.inspiredandroid.linuxcommandbibliotheca/)") - stream.appendLine("[![Web](https://raw.githubusercontent.com/SimonSchubert/LinuxCommandBibliotheca/master/art/web_badge.png)](https://linuxcommandlibrary.com)") - stream.appendLine() - stream.appendLine("### Mobile screenshots") - stream.appendLine() - stream.appendLine("

") - val mobileScreenshotFiles = - listOf("screen-1.png", "screen-2-dark.png", "screen-3.png", "screen-4-dark.png") - mobileScreenshotFiles.forEach { fileName -> - stream.appendLine("") - } - stream.appendLine("

") - val tabletScreenshotFiles = listOf("screen-1-tablet.png", "screen-2-tablet.png") - tabletScreenshotFiles.forEach { fileName -> - stream.appendLine("") - } - stream.appendLine() - stream.appendLine("### CLI screenshot") - stream.appendLine() - stream.appendLine("") - stream.appendLine() - stream.appendLine("Execute `gradle :cli:buildJar` to create jar file for Linux, Windows and Mac.") - stream.appendLine() - stream.appendLine("### Content") - stream.appendLine() - stream.appendLine("#### Categories") - stream.appendLine() - stream.appendLine( - databaseHelper.getBasics().joinToString { category -> - category.title - }, - ) - stream.appendLine() - stream.appendLine("#### Tips") - stream.appendLine() - stream.appendLine( - databaseHelper.getTips().joinToString { tip -> - tip.title - }, - ) - - stream.appendLine() - stream.appendLine("### CI/CD") - stream.appendLine() - stream.appendLine("[Github Action](.github/workflows/android.yml) to automatically create a new Github release with APK and JAR and upload an AAB to the Play Store.") - - stream.appendLine() - stream.appendLine("### Tests") - stream.appendLine() - stream.appendLine("Android Jetpack Compose screen tests: [ComposeTests.kt](android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeTests.kt)") - stream.appendLine() - stream.appendLine("Android Jetpack Compose deeplinking tests: [ComposeDeeplinkTests.kt](android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt)") - stream.appendLine() - stream.appendLine("Common code unit tests: [CommonTests.kt](common/src/commonTest/kotlin/CommonTests.kt)") - - stream.appendLine() - stream.appendLine("### Licensing") - stream.appendLine() - stream.appendLine("The source code is licensed under the Apache 2.0 license and the copyright of the man pages in the `database.db` file are copyrighted by their respective authors.") - - stream.appendLine() - stream.appendLine("### Thanks to") - stream.appendLine() - stream.appendLine("http://letsgokoyo.com - App Icon") - stream.appendLine() - stream.appendLine("https://www.commandlinefu.com - Lots of one-liners") - stream.appendLine() - stream.appendLine("https://icons8.com - Icons") - stream.appendLine() - stream.appendLine("https://tldr.sh - TLDR") - - stream.close() - } -} diff --git a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/Minifier.kt b/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/Minifier.kt deleted file mode 100644 index ddbe02c..0000000 --- a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/Minifier.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.linuxcommandlibrary.desktop - -import java.io.File -import java.nio.file.Files - -class Minifier { - - fun minifyScriptsAndSheets(isRelease: Boolean) { - val scriptsDir = File("html/scripts") - scriptsDir.mkdir() - val scripts = File("desktop/src/main/resources/scripts") - scripts.listFiles()?.forEach { - if (it.isFile) { - val file = File(scriptsDir, it.name) - file.delete() - if (isRelease) { - val minified = minifyJS(it.readText()) - file.writeText(minified) - } else { - Files.createLink(file.toPath(), it.toPath()) - } - } - } - val styleSheetsDir = File("html/stylesheets") - styleSheetsDir.mkdir() - val stylesheets = File("desktop/src/main/resources/stylesheets") - stylesheets.listFiles()?.forEach { - if (it.isFile) { - val file = File(styleSheetsDir, it.name) - file.delete() - if (isRelease) { - val minified = minifyCSS(it.readText()) - file.writeText(minified) - } else { - Files.createLink(file.toPath(), it.toPath()) - } - } - } - } - - private fun minifyCSS(css: String): String = css.replaceWhiteSpacesBeforeAndAfter(";") - .replaceWhiteSpacesBeforeAndAfter("}") - .replaceWhiteSpacesBeforeAndAfter("\\{") - .replaceWhiteSpacesBeforeAndAfter(":") - .replaceWhiteSpacesBeforeAndAfter(",") - - private fun minifyJS(js: String): String = js.replace("[\\n\\s].?//.*\\n".toRegex(), "") // will break if comment is after code - .replaceWhiteSpacesBeforeAndAfter(";") - .replaceWhiteSpacesBeforeAndAfter("}") - .replaceWhiteSpacesBeforeAndAfter("\\{") - .replaceWhiteSpacesBeforeAndAfter("=") - .replaceWhiteSpacesBeforeAndAfter("<") - .replaceWhiteSpacesBeforeAndAfter("-") - .replaceWhiteSpacesBeforeAndAfter(",") - .replaceWhiteSpacesBeforeAndAfter("\\+") - .replaceWhiteSpacesBeforeAndAfter("\\(") - .replaceWhiteSpacesBeforeAndAfter("\\)") - .replaceWhiteSpacesBeforeAndAfter("\\&") - .replaceWhiteSpacesBeforeAndAfter("\\>") - - private fun String.replaceWhiteSpacesBeforeAndAfter(value: String): String = replace("\\s*$value\\s*".toRegex(), value) -} diff --git a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/WebsiteBuilder.kt b/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/WebsiteBuilder.kt deleted file mode 100644 index b1a7975..0000000 --- a/desktop/src/main/kotlin/com/linuxcommandlibrary/desktop/WebsiteBuilder.kt +++ /dev/null @@ -1,1490 +0,0 @@ -package com.linuxcommandlibrary.desktop - -import com.linuxcommandlibrary.shared.CommandElement -import com.linuxcommandlibrary.shared.databaseHelper -import com.linuxcommandlibrary.shared.getCommandList -import com.linuxcommandlibrary.shared.getHtmlFileName -import com.linuxcommandlibrary.shared.getSortPriority -import com.linuxcommandlibrary.shared.initDatabase -import databases.BasicCategory -import kotlinx.coroutines.async -import kotlinx.html.ATarget -import kotlinx.html.DIV -import kotlinx.html.FlowContent -import kotlinx.html.HEAD -import kotlinx.html.HTMLTag -import kotlinx.html.HtmlTagMarker -import kotlinx.html.InputType -import kotlinx.html.LINK -import kotlinx.html.META -import kotlinx.html.ScriptCrossorigin -import kotlinx.html.UL -import kotlinx.html.a -import kotlinx.html.attributesMapOf -import kotlinx.html.b -import kotlinx.html.body -import kotlinx.html.br -import kotlinx.html.button -import kotlinx.html.classes -import kotlinx.html.div -import kotlinx.html.footer -import kotlinx.html.h1 -import kotlinx.html.h2 -import kotlinx.html.head -import kotlinx.html.html -import kotlinx.html.i -import kotlinx.html.id -import kotlinx.html.img -import kotlinx.html.input -import kotlinx.html.lang -import kotlinx.html.li -import kotlinx.html.link -import kotlinx.html.nav -import kotlinx.html.noScript -import kotlinx.html.onClick -import kotlinx.html.onKeyUp -import kotlinx.html.p -import kotlinx.html.script -import kotlinx.html.span -import kotlinx.html.stream.appendHTML -import kotlinx.html.style -import kotlinx.html.styleLink -import kotlinx.html.table -import kotlinx.html.td -import kotlinx.html.title -import kotlinx.html.tr -import kotlinx.html.ul -import kotlinx.html.unsafe -import kotlinx.html.visit -import org.json.JSONArray -import org.json.JSONObject -import java.io.File -import java.io.PrintStream -import java.util.Locale - -/* Copyright 2022 Simon Schubert - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -fun main() { - initDatabase() - - val minifier = Minifier() - val websiteBuilder = WebsiteBuilder() - - val folder = File("html") - folder.mkdir() - - websiteBuilder.createCommandsHtmlFile(folder) - - websiteBuilder.createBasicsHtmlFile(folder) - websiteBuilder.createBasicHtmlFiles(File(folder, "basic")) - - websiteBuilder.createTipsHtmlFile(folder) - websiteBuilder.createManHtmlFiles(File(folder, "man")) - - websiteBuilder.create404HtmlFile() - websiteBuilder.createPrivacyPolicyHtmlFile(folder) - websiteBuilder.createContactHtmlFile(folder) - websiteBuilder.createTermsAndConditionsHtmlFile(folder) - - websiteBuilder.createSitemap(folder) - - minifier.minifyScriptsAndSheets(true) -} - -class WebsiteBuilder { - - private val cacheVersion = 11 - - private val h2Regex by lazy { - "(

)(.*?)(

)".toRegex() - } - private val quoteRegex by lazy { - "`(.*?)`".toRegex() - } - private val commandRegex by lazy { - "([^\\s,]+)\\(([0-9]|1L|1M|p)\\)".toRegex() - } - private val htmlTagRegex by lazy { - "<[^>]*>".toRegex() - } - - fun createCommandsHtmlFile(folder: File) { - println("Create index html") - - val file = File(folder, "commands.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - stream.appendHTML().html { - lang = "en" - head { - commonMeta() - val title = "Commands | Linux Command Library" - uncommonMeta( - title = title, - description = "Handy cheat sheets with linux tips, terminal basics and thousands of man pages.", - url = "https://linuxcommandlibrary.com", - keywords = "linux,cmd,tips,man,commands", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - script(src = "/scripts/search.js?v=$cacheVersion") { - defer = true - } - - noScript { - style { - unsafe { +"#search-wrapper { display: none; }" } - } - } - } - body { - header(selectedIndex = 0) - - contentWrapper { - div { - id = "content" - style = "width: 100%; padding: 12px; align-self: start" - div { - id = "search-wrapper" - input { - type = InputType.text - id = "search" - onKeyUp = "search()" - placeholder = "Search for commands" - autoComplete = "false" - } - } - div { - id = "commandlist" - var currentFirstLetter = "" - databaseHelper.getCommands().forEach { - if (it.name.lowercase().first().toString() != currentFirstLetter) { - currentFirstLetter = it.name.lowercase().first().toString() - div { - classes = classes + "headline" - text(currentFirstLetter.uppercase()) - } - } - a("man/${it.name.lowercase()}") { - attributes["data-c"] = it.name.lowercase() - text(it.name) - } - it.name - } - } - div { - id = "no-results" - text("No results") - } - } - } - - footer() - } - } - stream.close() - } - - fun createBasicsHtmlFile(folder: File) { - println("Create basics html") - - val file = File(folder, "index.html") - file.delete() - val stream = PrintStream(file) - - val basicCategories = databaseHelper.getBasics() - - stream.appendLine("") - stream.appendHTML().html { - lang = "en" - head { - commonMeta() - - val title = "Basics | Cheat sheets | Linux Command Library" - uncommonMeta( - title = title, - description = "Handy cheat sheets with linux tips and terminal basics about System control, Users, Files, Package managers, Video and Audio, Hacking tools, Terminal games and many more categories.", - url = "https://linuxcommandlibrary.com/${folder.name}/${file.nameWithoutExtension}", - keywords = "linux,cmd,basics,terminal,console,cheat sheets,tips,${ - basicCategories.joinToString( - ",", - ) { it.title } - }", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - } - body { - header(selectedIndex = 1) - - contentWrapper { - div { - ul { - classes = setOf("grid-container") - - basicCategories.forEach { - li { - classes = setOf("grid-item") - a("basic/${it.getHtmlFileName()}") { - div { - i { - classes = setOf("invert-color") - style = - "background-image: url(\"images/${it.getIconResource()}\");" - } - h2 { - text(it.title) - } - } - } - } - } - } - } - } - - footer() - } - } - stream.close() - } - - fun createBasicHtmlFiles(folder: File) { - folder.mkdir() - - val basicCategories = databaseHelper.getBasics() - val totalCount = basicCategories.count() - - basicCategories.forEachIndexed { index, category -> - print("\rCreate basic category html ${index + 1}/$totalCount") - - val groups = databaseHelper.getBasicGroupsByQuery(category.id) - - val file = File(folder, "${category.getHtmlFileName()}.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - stream.appendHTML().html { - lang = "en" - head { - val title = "${category.title} | Basic | Cheat sheet | Linux Command Library" - commonMeta() - uncommonMeta( - title = title, - description = category.getDescription(), - url = "https://linuxcommandlibrary.com/${folder.name}/${file.nameWithoutExtension}", - keywords = getKeywordsForBasic(category), - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - script(src = "/scripts/copy.js?v=$cacheVersion") { - defer = true - } - - noScript { - style { - unsafe { +".copy-button { display: none; }" } - } - } - - if (category.title == "One-liners") { - style { - unsafe { +".masonry{-webkit-column-width: 400px !important;-moz-column-width: 400px !important;column-width: 400px !important;}" } - } - } else { - script(type = "application/ld+json") { - val faqJson = JSONObject() - faqJson.put("@context", "https://schema.org") - faqJson.put("@type", "FAQPage") - val answerArray = JSONArray() - groups.forEach { group -> - val answerJson = JSONObject() - answerJson.put("@type", "Question") - answerJson.put("name", group.description) - val acceptedAnswerJson = JSONObject() - acceptedAnswerJson.put("@type", "Answer") - acceptedAnswerJson.put( - "text", - databaseHelper.getBasicCommands(group.id).first().command, - ) - answerJson.put("acceptedAnswer", acceptedAnswerJson) - answerArray.put(answerJson) - } - faqJson.put("mainEntity", answerArray) - } - } - } - body { - header(1) - - contentWrapper { - div { - h1 { - text(category.title) - } - div { - classes = setOf("masonry") - groups.forEach { group -> - div { - classes = setOf("code-group") - h2 { - a("/${folder.name}/${file.nameWithoutExtension}#${group.id}") { - id = group.id.toString() - text(group.description) - } - } - databaseHelper.getBasicCommands(group.id) - .forEach { command -> - if (listOf( - "VIM Texteditor", - "Emacs Texteditor", - "Nano Texteditor", - "Pico Texteditor", - "Micro Texteditor", - ).contains(category.title) - ) { - table { - command.command.split("\n").forEach { - tr { - it.split(" - ").forEach { - td { - text(it) - } - } - } - } - } - } else if (listOf( - "Terminal games", - "Fun", - ).contains(category.title) - ) { - code( - "$ ${ - command.command.replace( - "\\n", - "
", - ) - }", - command.mans, - true, - ) - } else { - code( - "$ ${ - command.command.replace( - "\\n", - "
", - ) - }", - command.mans, - ) - } - } - } - } - } - } - } - - tooltip() - footer() - } - } - stream.close() - } - println() - } - - fun createTipsHtmlFile(folder: File) { - println("Create tips html") - - folder.mkdir() - - val file = File(folder, "tips.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - stream.appendHTML().html { - lang = "en" - head { - commonMeta() - val title = "Tips | Cheat sheets | Linux Command Library" - uncommonMeta( - title = title, - description = "Handy cheat sheets with linux tips and terminal basics.", - url = "https://linuxcommandlibrary.com/${file.nameWithoutExtension}", - keywords = "linux,cmd,useful,terminal,tips,cheat", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - script(src = "/scripts/copy.js?v=$cacheVersion") { - defer = true - } - - noScript { - style { - unsafe { +".copy-button { display: none; }" } - } - } - } - - body { - header(2) - - contentWrapper { - div { - classes = setOf("masonry") - - databaseHelper.getTips().forEach { tip -> - div { - classes = setOf("code-group") - - h2 { - a("/${file.nameWithoutExtension}#${tip.id}") { - id = tip.id.toString() - text(tip.title) - } - } - - var isTable = false - - fun closeTable() { - isTable = false - unsafe { - +"" - } - } - - fun startTable() { - isTable = true - unsafe { - +"" - } - } - - databaseHelper.getTipSections().filter { it.tip_id == tip.id }.forEach { - if (it.type != 3L && isTable) { - closeTable() - } - if (it.type == 3L && !isTable) { - startTable() - } - when (it.type) { - 0L -> { - span { - unsafe { - +it.data1.replace("\\n", "
") - } - } - br - } - - 1L -> { - code(it.data1.replace("\\n", "
"), it.extra) - } - - 3L -> { - unsafe { - +"" - } - unsafe { - +"" - } - } - } - } - if (isTable) { - closeTable() - } - } - } - } - } - tooltip() - footer() - } - } - stream.close() - } - - fun createManHtmlFiles(folder: File) { - folder.mkdir() - - val commands = databaseHelper.getCommands() - val totalCount = commands.count() - - commands.forEachIndexed { index, command -> - print("\rCreate mans html ${index + 1}/$totalCount") - - val file = File(folder, "${command.name.lowercase()}.html") - - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - stream.appendHTML().html { - lang = "en" - head { - commonMeta() - - val title = "${command.name} man | Linux Command Library" - uncommonMeta( - title = title, - description = "${command.name} linux command man page: ${command.description}", - url = "https://linuxcommandlibrary.com/${folder.name}/${file.nameWithoutExtension}", - keywords = "linux,man,page,command,manual,${command.name}", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - script(src = "/scripts/copy.js?v=$cacheVersion") { - defer = true - } - script(type = "application/ld+json") { - val applicationJson = JSONObject() - applicationJson.put("@context", "https://schema.org") - applicationJson.put("@type", "SoftwareApplication") - applicationJson.put("name", command.name) - applicationJson.put("operatingSystem", "LINUX") - unsafe { - +applicationJson.toString() - } - } - - noScript { - style { - unsafe { - +".toggle-all-button { display: none; }" - +".copy-button { display: none; }" - } - } - } - } - body { - header(selectedIndex = 0) - - contentWrapper { - div { - id = "content" - h1 { - text(command.name) - } - h2 { - classes = setOf("subtitle") - text(command.description) - } - - databaseHelper.getSections(command.id).sortedBy { it.getSortPriority() } - .forEach { section -> - h2 { - onClick = "togglePanel(this)" - classes = setOf("accordion-button", "active") - val sectionId = - section.title.lowercase(Locale.US).replace(" ", "-") - a("/man/${command.name.lowercase(Locale.US)}#$sectionId") { - id = sectionId - text(section.title) - } - } - div { - classes = setOf("panel") - when (section.title) { - "SEE ALSO" -> { - p { - val elements = - getSeeAlsoSectionElements(section.content) - elements.forEach { element -> - when (element) { - is CommandElement.Man -> { - a("/man/${element.man}") { - title = - "${element.man} man page" - text(element.man) - } - } - - is CommandElement.Text -> { - unsafe { - +element.text - } - } - - else -> {} - } - } - } - } - - "TLDR" -> { - p { - unsafe { - +sanitizeHtml(section.content.addAnchorAndCodeStyle(file.nameWithoutExtension)) - } - } - } - - else -> { - p { - unsafe { - +sanitizeHtml(section.content) - } - } - } - } - } - } - - button { - onClick = "toggleAll(this)" - classes = setOf("toggle-all-button") - text("COLLAPSE ALL") - } - } - } - - tooltip() - footer() - - script(src = "/scripts/man.js?v=$cacheVersion") { - defer = true - } - } - } - stream.close() - } - println() - } - - fun createTermsAndConditionsHtmlFile(folder: File) { - println("Create terms and conditions html") - - folder.mkdir() - - val file = File(folder, "terms-conditions.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - - stream.appendHTML().html { - lang = "en" - head { - commonMeta(adSense = false) - val title = "Terms and Conditions | Linux Command Library" - uncommonMeta( - title = title, - description = "", - url = "https://linuxcommandlibrary.com/${file.nameWithoutExtension}", - keywords = "", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - } - - body { - header(-1) - - div { - id = "content" - - h1 { +"Terms and Conditions" } - - h2 { +"Educational Purpose Only" } - p { - +"The content on Linux Command Library is provided solely for educational and informational purposes. It is intended to assist users in learning about Linux commands." - } - - h2 { +"Disclaimer of Liability" } - p { - +"The commands on this website can be powerful and may cause data loss or system damage if misused. Users are responsible for verifying and safely using the commands. Linux Command Library is not liable for any damage or loss resulting from the use or misuse of this information." - } - - h2 { +"Copyright and Credits" } - p { - +"Man pages referenced on this site are copyrighted by their respective authors and used under fair use principles for educational reference. We gratefully acknowledge inspiration and contributions from " - a(href = "https://tldr.sh/") { +"TLDR" } - +" and " - a(href = "https://www.commandlinefu.com/") { +"commandlinefu.com" } - +"." - } - } - - footer(showAd = false) - } - } - stream.close() - } - - fun createPrivacyPolicyHtmlFile(folder: File) { - println("Create privacy html") - - folder.mkdir() - - val file = File(folder, "privacy-policy.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - - stream.appendHTML().html { - lang = "en" - head { - commonMeta(adSense = false) - val title = "Privacy Policy | Linux Command Library" - uncommonMeta( - title = title, - description = "", - url = "https://linuxcommandlibrary.com/${file.nameWithoutExtension}", - keywords = "", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - } - - body { - header(-1) - - div { - id = "content" - - h1 { +"Privacy Policy" } - p { +"Last Updated: April 19, 2025" } - - h2 { +"1. No Data Collection or Tracking" } - p { +"Our website does not use cookies, web beacons, or any other tracking technologies to collect personal information about your browsing activities." } - p { +"We do not collect any personal data from users. There are no contact forms, email sign-ups, or other mechanisms that gather personal information on our website." } - - h2 { +"2. Future Use of Cookies (Google AdSense)" } - p { - +"If our website is approved for Google AdSense, we may use cookies to serve personalized advertisements. These cookies would be managed by Google and are subject to " - a(href = "https://policies.google.com/privacy") { +"Google’s Privacy Policy" } - +". If this occurs, we will update this Privacy Policy to reflect the change and provide details on how cookies are used for advertising purposes." - } - p { +"You will be able to manage your ad preferences through Google’s Ad Settings." } - - h2 { +"3. Your Rights (GDPR Compliance)" } - p { +"For EU Users: If you are located in the European Union, you have the right to access, correct, or delete any personal data we may hold about you. Since we do not collect personal data through tracking or forms, no such data is stored." } - p { +"Cookie Consent (Future): If we introduce cookies for advertising in the future, we will implement a cookie consent mechanism for EU users to ensure compliance with GDPR." } - - h2 { +"4. Contact Us" } - p { +"If you have any questions or concerns about this Privacy Policy, you can contact us at [your email address]." } - - h2 { +"5. Changes to This Policy" } - p { +"We may update this Privacy Policy from time to time, especially if we introduce new features or services that affect data handling (e.g., Google AdSense). Any changes will be posted on this page with an updated 'Last Updated' date." } - } - - footer(showAd = false) - } - } - stream.close() - } - - fun createContactHtmlFile(folder: File) { - println("Create contact html") - - folder.mkdir() - - val file = File(folder, "contact.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - - stream.appendHTML().html { - lang = "en" - head { - commonMeta(adSense = false) - val title = "Contact | Linux Command Library" - uncommonMeta( - title = title, - description = "", - url = "https://linuxcommandlibrary.com/${file.nameWithoutExtension}", - keywords = "", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - } - - body { - header(-1) - - div { - id = "content" - - h1 { +"Contact Us" } - p { - +"Have questions, feedback, or issues about Linux Command Library?" - } - p { - +"Please reach out to us at " - a(href = "mailto:info@linuxcommandlibrary.com") { +"info@linuxcommandlibrary.com" } - +". We aim to respond within 2-3 business days." - } - p { - +"For more information about our site, please review our " - a(href = "/privacy-policy") { +"Privacy Policy" } - +" and " - a(href = "/terms-and-conditions") { +"Terms and Conditions" } - +"." - } - } - - footer(showAd = false) - } - } - stream.close() - } - - private fun sanitizeHtml(content: String): String = content.replace(Regex("(?i)<(html|head|title|body)[^>]*>.*?|<(html|head|title|body)[^>]*>"), "") - .replace(Regex("(?i)"), "") - - /** - * Find and link man pages if command exists in database. Example: ps(1), regex(7), signal(7) - */ - private fun getSeeAlsoSectionElements(content: String): List { - var text = content.replace(htmlTagRegex, "") - repeat(10) { - text = text.replace(" ($it)", "($it)") - } - val mans = text.getCommaSeparatedMans() - - return text.getCommandList( - mans, - hasBrackets = true, - checkExisting = true, - ) - } - - /** - * Return comma separated list of commands. Example: ps(1),man(1) -> ps,man - */ - private fun String.getCommaSeparatedMans(): String { - val matches = commandRegex.findAll(this) - return matches.mapNotNull { - it.groups[1]?.value - }.sortedByDescending { it.length }.joinToString(",") - } - - fun create404HtmlFile() { - println("Create 404 html") - - val file = File("html/error_404.html") - file.delete() - val stream = PrintStream(file) - - stream.appendLine("") - stream.appendHTML().html { - lang = "en" - head { - commonMeta(adSense = false) - val title = "404 command not found | Linux Command Library" - uncommonMeta( - title = title, - description = "Handy cheat sheets with linux tips, terminal basics and thousands of man pages.", - url = "https://linuxcommandlibrary.com", - keywords = "linux,cmd,tips,man,commands", - ) - - styleLink("/stylesheets/main.css?v=$cacheVersion") - } - body { - header(selectedIndex = -1) - - div { - style = - "display: flex;height: 500px;justify-content: center;align-items: center;flex-direction: column;" - h1 { - style = "margin: inherit;" - text("404 command not found") - } - - img { - style = "margin-top:12px;" - src = "/images/icons8-404.svg" - width = "100" - height = "100" - alt = "not found" - } - } - - footer(false) - } - } - } - - private fun UL.headerNav( - title: String, - href: String, - index: Int, - selectedIndex: Int, - ): FlowContent { - li { - a(href) { - if (selectedIndex == index) { - classes = setOf("selected") - } - text(title) - } - } - return this - } - - private fun FlowContent.header(selectedIndex: Int) { - div { - id = "top-border" - - div { - a("/") { - title = "logo" - id = "logo-icon-wrapper" - i { - style = "background-image: url('/images/logo.png');" - classes = setOf("logo-icon") - } - } - div { - classes = setOf("title") - span { - text("Linux") - } - span { - text("Command") - } - span { - text("Library") - } - } - - div { - a("https://github.com/SimonSchubert/LinuxCommandLibrary") { - target = ATarget.blank - rel = "noopener" - img { - src = "/images/logo-github.svg" - width = "25" - height = "25" - } - } - a("https://f-droid.org/en/packages/com.inspiredandroid.linuxcommandbibliotheca") { - target = ATarget.blank - rel = "noopener" - img { - src = "/images/icon-fdroid.svg" - width = "25" - height = "25" - } - } - a("https://play.google.com/store/apps/details?id=com.inspiredandroid.linuxcommandbibliotheca") { - target = ATarget.blank - rel = "noopener" - img { - src = "/images/icon-playstore.svg" - width = "25" - height = "25" - } - } - } - } - } - nav { - ul { - headerNav("Basics", "/", 1, selectedIndex) - headerNav("Tips", "/tips", 2, selectedIndex) - headerNav("Commands", "/commands", 0, selectedIndex) - } - } - } - - fun createSitemap(folder: File) { - val file = File(folder, "sitemap.xml") - file.delete() - - val stream = PrintStream(file) - stream.print("") - stream.print(getSitemapUrlNode("")) - stream.print(getSitemapUrlNode("tips")) - stream.print(getSitemapUrlNode("commands")) - databaseHelper.getBasics().forEach { - stream.print(getSitemapUrlNode("basic/${it.getHtmlFileName()}")) - } - databaseHelper.getCommands().forEach { - stream.print(getSitemapUrlNode("man/${it.name}")) - } - stream.print("") - stream.close() - } - - private fun getSitemapUrlNode(urlPart: String): String = "" + - "https://linuxcommandlibrary.com/$urlPart" + - "" - - private fun HEAD.commonMeta(adSense: Boolean = true) { - meta(charset = "utf-8") - meta(name = "viewport", content = "width=device-width, initial-scale=1") - link(rel = "apple-touch-icon", sizes = "180x180", href = "/apple-touch-icon.png") - link(rel = "icon", type = "image/png", sizes = "32x32", href = "/favicon-32x32.png") - link(rel = "icon", type = "image/png", sizes = "16x16", href = "/favicon-16x16.png") - link(rel = "manifest", href = "/site.webmanifest") - meta(name = "msapplication-TileColor", content = "#da532c") - meta(name = "theme-color", content = "#ffffff") - if (adSense) { - script { - src = - "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3003920357099437" - async = true - crossorigin = ScriptCrossorigin.anonymous - } - } - } - - private fun HEAD.uncommonMeta( - title: String, - description: String, - url: String, - keywords: String, - ) { - title(title) - - meta(name = "description", content = description) - meta(name = "keywords", content = keywords) - link(rel = "canonical", href = url) - - meta(property = "og:title", content = title) - meta(property = "og:type", content = "website") - meta(property = "og:url", content = url) - meta(property = "og:image", content = "https://linuxcommandlibrary.com/images/preview.jpg") - meta(property = "og:description", content = description) - meta(property = "og:site_name", content = "Linux Command Library") - meta(property = "og:locale", content = "en_US") - - meta(property = "twitter:card", content = "summary") - meta(property = "twitter:title", content = title) - meta(property = "twitter:description", content = description) - meta( - property = "twitter:image", - content = "https://linuxcommandlibrary.com/images/preview.jpg", - ) - } - - private inline fun HTMLTag.link( - href: String? = null, - rel: String? = null, - type: String? = null, - sizes: String?, - crossinline block: LINK.() -> Unit = {}, - ): Unit = LINK( - attributesMapOf("href", href, "rel", rel, "type", type, "sizes", sizes), - consumer, - ).visit(block) - - data class Ad(val imageUrl: String, val url: String, val backgroundColor: String) - val ads = listOf( - Ad("linode-vertical.webp", "/linode-2025", "#ea9230"), - Ad("digitalocean-vertical.webp", "/digitalocean-2025", "#173a62"), - Ad("proton-free-vertical.webp", "/proton-free-2025", "#01a4e8"), - Ad("proton-paid-vertical.webp", "/proton-paid-2025", "#f1c522"), - ) - - private fun FlowContent.contentWrapper(content: DIV.() -> Unit = {}): FlowContent { - val randomAds = ads.shuffled().take(2) - div { - id = "content-wrapper" - randomAds[0].let { ad -> - div { - classes = setOf("side-panel") - style = "background-color: ${ad.backgroundColor}" - a { - href = ad.url - img { - src = "/images/af/${ad.imageUrl}" - width = "200" - } - } - } - } - content() - randomAds[1].let { ad -> - div { - classes = setOf("side-panel") - style = "background-color: ${ad.backgroundColor}" - a { - href = ad.url - img { - src = "/images/af/${ad.imageUrl}" - width = "200" - } - } - } - } - } - return this - } - - private fun FlowContent.footer(showAd: Boolean = true): FlowContent { - if (showAd) { - div { - classes = setOf("bottom-panel") - - a("/linode-2025") { - target = ATarget.blank - img { - style = "max-width: calc(100% - 4px);" - src = "/images/af/linode-horizontal.webp" - attributes["loading"] = "lazy" - width = "600" - } - } - } - } - footer { - p { - a { - target = ATarget.self - rel = "noopener" - href = "/privacy-policy" - text("Privacy Policy") - } - text(" | ") - a { - target = ATarget.self - rel = "noopener" - href = "/terms-conditions" - text("Terms and Conditions") - } - text(" | ") - a { - target = ATarget.self - rel = "noopener" - href = "/contact" - text("Contact") - } - } - - a("https://play.google.com/store/apps/details?id=com.inspiredandroid.linuxcommandbibliotheca") { - style = "margin-right: 4px;" - target = ATarget.blank - rel = "noopener" - img { - src = "/images/google-play-download.svg" - alt = "Google Play Store" - classes = setOf("download-icon") - width = "169" - height = "50" - } - } - a("https://f-droid.org/en/packages/com.inspiredandroid.linuxcommandbibliotheca") { - style = "margin-left: 4px;" - target = ATarget.blank - rel = "noopener" - img { - src = "/images/f-droid-download.png" - alt = "F-Droid Store" - classes = setOf("download-icon") - width = "168" - height = "50" - } - } - div { - style = - "width: 100%; justify-content: center; padding-top: 6px; display: flex; align-items: center; gap: 6px;" - text("My other projects: ") - div { - classes = setOf("project") - a("https://adahub.io") { - target = ATarget.blank - rel = "noopener" - img { - src = "https://adahub.io/favicon.svg" - alt = "adahub.io" - width = "30" - height = "30" - } - } - br - text("Blockchain") - } - div { - classes = setOf("project") - a("https://simonschubert.github.io/YogaBase/") { - target = ATarget.blank - rel = "noopener" - img { - src = "https://simonschubert.github.io/YogaBase/favicon.svg" - alt = "" - width = "30" - height = "30" - } - } - br - text("Yoga") - } - div { - classes = setOf("project") - a("https://betabase.fun") { - target = ATarget.blank - rel = "noopener" - img { - src = "https://betabase.fun/images/icon.png" - alt = "betabase.fun" - width = "30" - height = "30" - } - } - br - text("Climbing") - } - } - } - return this - } - - private fun FlowContent.tooltip(): FlowContent { - div { - classes = setOf("tooltip") - b { - text("Copied to clipboard") - } - } - return this - } - - private fun FlowContent.code( - command: String, - mans: String, - isMonospace: Boolean = false, - ): FlowContent { - div { - classes = setOf("code-wrapper") - span { - classes = setOf("code") - if (isMonospace) { - style = "font-family: 'Courier New', Courier, monospace;font-size:14px;" - } - command.getCommandList(mans).forEach { element -> - when (element) { - is CommandElement.Man -> { - a("/man/${element.man}") { - title = "${element.man} man page" - text(element.man) - } - } - - is CommandElement.Text -> { - element.text.split("
").map { it.replace("$ ", "$ ") } - .forEachIndexed { index, s -> - if (index != 0) { - br - } - s.split(" ").forEachIndexed { index2, s2 -> - if (index2 != 0) { - unsafe { - +" " - } - } - text(s2) - } - } - } - - is CommandElement.Url -> { - a(element.url) { - target = ATarget.blank - rel = "noopener" - text(element.command) - } - } - } - } - } - div { - if (isMonospace) { - onClick = - "javascript:copy('$mans')" - } else { - onClick = - "javascript:copy('${ - command.split("
").first().drop(2).replace("'", "'") - .replace("\n", "").trim() - }')" - } - classes = setOf("copy-button") - img { - src = "/images/icon-copy.svg" - alt = "copy" - width = "24" - height = "24" - } - } - } - return this - } - - @HtmlTagMarker - inline fun HTMLTag.meta( - property: String? = null, - name: String? = null, - content: String? = null, - charset: String? = null, - httpEquiv: String? = null, - crossinline block: META.() -> Unit = {}, - ): Unit = META( - attributesMapOf( - "property", - property, - "name", - name, - "content", - content, - "charset", - charset, - "http-equiv", - httpEquiv, - ), - consumer, - ).visit(block) - - private fun BasicCategory.getIconResource(): String = when (title) { - "One-liners" -> "icon-hand_with_pen.svg" - "System information" -> "icon-system_task.svg" - "System control" -> "icon-settings.svg" - "Users & Groups" -> "icon-user.svg" - "Files & Folders" -> "icon-file.svg" - "Printing" -> "icon-print.svg" - "Network" -> "icon-network_card.svg" - "Search & Find" -> "icon-search.svg" - "GIT" -> "icon-git.svg" - "SSH" -> "icon-console.svg" - "Video & Audio" -> "icon-video_trimming.svg" - "Package manager" -> "icon-package.svg" - "Hacking tools" -> "icon-skull.svg" - "Terminal games" -> "icon-controller.svg" - "VIM" -> "icon-vim.svg" - "Emacs" -> "icon-emacs.svg" - "Nano" -> "icon-nano.svg" - "Pico" -> "icon-pico.svg" - "Crypto currencies" -> "icon-bitcoin.svg" - "Input" -> "icon-mouse.svg" - "JSON" -> "icon-json.svg" - "Fun" -> "icon-fun.svg" - "VIM Texteditor" -> "icon-text-edit.svg" - "Emacs Texteditor" -> "icon-text-edit.svg" - "Nano Texteditor" -> "icon-text-edit.svg" - "Pico Texteditor" -> "icon-text-edit.svg" - "Micro Texteditor" -> "icon-text-edit.svg" - else -> "" - } - - private fun BasicCategory.getDescription(): String = when (title) { - "One-liners" -> "Useful linux command line one liners" - "System information" -> "System and battery/cpu/memory/disk usage info on Linux " - "System control" -> "Lock, unlock, start/stop bluetooth/wifi, shutdown, reboot system" - "Users & Groups" -> "Create, remove, modify and list Linux groups and users" - "Files & Folders" -> "Create, delete, list, show and change Linux files and folders" - "Printing" -> "Print, view, start and cancel printing jobs on Linux" - "Network" -> "Configure, list, trace, sockets, wifi networks on Linux" - "Search & Find" -> "Search and find files by phrase, date and size on Linux" - "GIT" -> "Commit, push, create, delete and undo with git on Linux" - "SSH" -> "Connect, forward, push and pull files via SSH" - "Video & Audio" -> "Convert, volume, play, screenshot, webcam on Linux" - "Package manager" -> "Install, update, upgrade, remove packages on Linux" - "Hacking tools" -> "Hacking, forensics and exploitation tools for Linux" - "Terminal games" -> "Terminal games on Linux" - "VIM Texteditor" -> "Working with vim on the Linux command line" - "Emacs Texteditor" -> "Working with emacs on the Linux command line" - "Nano Texteditor" -> "Working with nano on the Linux command line" - "Pico Texteditor" -> "Working with pico on the Linux command line" - "Micro Texteditor" -> "Working with micro on the Linux command line" - "Crypto currencies" -> "Miners, wallets and trading bots for Linux" - "Input" -> "Type keys and move mouse via the Linux command line" - "JSON" -> "Print, select, modify, delete and create json files on cmd" - "Fun" -> "Fun on the linux command line" - else -> "" - } - - private fun getKeywordsForBasic(category: BasicCategory): String = when (category.title) { - "One-liners" -> "linux,list,useful,oneliners,commands,cmd" - "Input" -> "linux,move,click,mouse,type,text,xdotool,ydotool,read,copy,clipboard" - "System information" -> "linux,system,info,disk,bluetooth,cpu,memory,battery" - "System control" -> "linux,control,lock,unlock,reboot,shutdown,start,stop,wifi,bluetooth" - "Users & Groups" -> "linux,create,delete,user,group,list,info" - "Files & Folders" -> "linux,create,edit,delete,file,folder,permission,list" - "Printing" -> "linux,print,file,cancel,job,status,queue" - "JSON" -> "linux,json,pretty,print,select,put,delete,create" - "Network" -> "linux,network,wifi,password,ip,interfaces,sockets" - "Search & Find" -> "linux,find,search,pattern,files,path,phrase" - "GIT" -> "linux,create,clone,repository,tag,checkout,delete,commit" - "SSH" -> "linux,connect,ssh,push,pull,forwarding" - "Video & Audio" -> "linux,screenshot,webcam,sounds,video,convert,image" - "Package manager" -> "linux,install,file,repository,find,package,upgrade" - "Hacking tools" -> "linux,password,forensics,sniffing,spoofing,exploit,vulnerability" - "Crypto currencies" -> "linux,minters,wallets,coin,trading,bots" - "VIM Texteditor" -> "linux,insert,search,edit,replace,navigation" - "Emacs Texteditor" -> "linux,emacs,usage,buffers,navigation" - "Nano Texteditor" -> "linux,nano,info,navigation,edit,input,output" - "Pico Texteditor" -> "linux,pico,navigation,usage,input,output" - "Micro Texteditor" -> "linux,pico,navigation,usage,input,output" - "Terminal games" -> "linux,terminal,games,list,rogue" - else -> throw Exception("${category.title} not found") - } - - private fun String.addAnchorAndCodeStyle(fileName: String): String { - var content = this - var matches = quoteRegex.findAll(content) - matches.forEach { - val command = it.value.replace("`", "").replace("'", "'").replace(">", ">") - .replace("<", "<") - .replace("\"", """) - content = content.replace( - it.value, - "
$ $command
\"copy\"
", - ) - } - - matches = h2Regex.findAll(content) - matches.forEachIndexed { index, matchResult -> - val text = - matchResult.value.replace("

", "").replace("

", "").replace(">", ">") - .replace("<", "<") - content = - content.replace( - matchResult.value, - "

$text

", - ) - } - - return content - } -} diff --git a/desktop/src/main/resources/scripts/copy.js b/desktop/src/main/resources/scripts/copy.js deleted file mode 100644 index 7c6b142..0000000 --- a/desktop/src/main/resources/scripts/copy.js +++ /dev/null @@ -1,42 +0,0 @@ -var timeout; - -function copy(text) { - var inp = document.createElement('input'); - document.body.appendChild(inp); - inp.value = unEscapeHtml(text); - inp.select(); - document.execCommand('copy', false); - inp.remove(); - - clearTimeout(timeout); - hide(); - timeout = setTimeout(function() { - hide(); - }, 1500); - setTimeout(function() { - show(); - }, 100); -} - -function show() { - var element = document.getElementsByClassName("tooltip")[0]; - element.classList.remove("hidden"); - element.classList.add("visible"); -} - -function hide() { - var element = document.getElementsByClassName("tooltip")[0]; - element.classList.remove("visible"); - element.classList.add("hidden"); -} - -function unEscapeHtml(unsafe) { - return unsafe - .replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, "\"") - .replace(/'/g, "'") - .replace(///g, "index.html") - .replace(/\\/g, "\\\\"); -} \ No newline at end of file diff --git a/desktop/src/main/resources/scripts/man.js b/desktop/src/main/resources/scripts/man.js deleted file mode 100644 index 4ada968..0000000 --- a/desktop/src/main/resources/scripts/man.js +++ /dev/null @@ -1,65 +0,0 @@ -var accordionButtons = document.getElementsByClassName("accordion-button"); -var toggleAllButton = document.getElementsByClassName("toggle-all-button")[0]; - -function togglePanel(param) { - param.classList.toggle("active"); - updatePanel(param); - - if (allCollapsed()) { - toggleAllButton.classList.remove("active"); - toggleAllButton.classList.add("active"); - toggleAllButton.innerText = "EXPAND ALL"; - } else if (isAnyExpanded()) { - toggleAllButton.classList.remove("active"); - toggleAllButton.innerText = "COLLAPSE ALL"; - } -} - -function toggleAll(param) { - param.classList.toggle("active"); - - if (param.classList.contains("active")) { - param.innerText = "EXPAND ALL"; - updateList(true); - } else { - param.innerText = "COLLAPSE ALL"; - updateList(false); - } -} - -function updateList(collapse) { - for (var i = 0; i < accordionButtons.length; i++) { - accordionButtons[i].classList.remove("active"); - if (!collapse) { - accordionButtons[i].classList.add("active"); - } - updatePanel(accordionButtons[i]); - } -} - -function updatePanel(button) { - var panel = button.nextElementSibling; - if (button.classList.contains("active")) { - panel.style.display = "block"; - } else { - panel.style.display = "none"; - } -} - -function isAnyExpanded() { - for (var i = 0; i < accordionButtons.length; i++) { - if (accordionButtons[i].classList.contains("active")) { - return true; - } - } - return false; -} - -function allCollapsed() { - for (var i = 0; i < accordionButtons.length; i++) { - if (accordionButtons[i].classList.contains("active")) { - return false; - } - } - return true; -} \ No newline at end of file diff --git a/desktop/src/main/resources/scripts/search.js b/desktop/src/main/resources/scripts/search.js deleted file mode 100644 index 1723c5d..0000000 --- a/desktop/src/main/resources/scripts/search.js +++ /dev/null @@ -1,83 +0,0 @@ -var input, filter, ul, li, a, i, headers, value, index, lastSearch = ""; - -window.onload = (event) => { - input = document.getElementById('search'); - ul = document.getElementById("commandlist"); - li = ul.getElementsByTagName('a'); - headers = ul.getElementsByTagName('div'); -}; - -document.addEventListener('keyup', (e) => { - if (e.keyCode == 38) { - focusPreviousTabStop(); - } else if (e.keyCode == 40) { - focusNextTabStop(); - } -}); - -function getVisibleTabs() { - var universe = document.querySelectorAll('#commandlist a'); - return Array.prototype.filter.call(universe, function(item) {return item.parentNode.style.display !== "none"}); -} - -function focusNextTabStop() { - var tabs = getVisibleTabs(); - var index = tabs.indexOf(document.activeElement); - return (tabs[index + 1] || tabs[0]).focus(); -} - -function focusPreviousTabStop() { - var tabs = getVisibleTabs(); - var index = tabs.indexOf(document.activeElement); - if(index == 0) { - input.focus(); - } else { - (tabs[index - 1] || tabs[0]).focus(); - } -} - -function search(){ - return Promise.resolve() - .then(function() { - setTimeout(function() { - filter = input.value.toLowerCase(); - if(lastSearch === filter) { - return - } - if(lastSearch === "") { - for (i = 0; i < headers.length; i++) { - headers[i].style.display = "none"; - } - } - if(filter === "") { - for (i = 0; i < headers.length; i++) { - headers[i].style.display = ""; - } - } - lastSearch = filter; - var numberOfResults = 0; - for (i = 0; i < li.length; i++) { - value = li[i].getAttribute('data-c'); - index = value.indexOf(filter); - if (index > -1) { - value = value.substring(0, index) + "" + value.substring(index, index + filter.length) + "" + value.substring(index + filter.length); - li[i].innerHTML = value; - numberOfResults++; - li[i].style.display = ""; - } else { - li[i].style.display = "none"; - } - } - - var noresults = document.getElementById("no-results"); - if(numberOfResults == 0) { - noresults.innerHTML = "No commands found for '" + filter + "'"; - noresults.style.display = "block"; - ul.style.display = "none"; - } else { - noresults.style.display = "none"; - ul.style.display = "block"; - } - }, 0); - }); -} \ No newline at end of file diff --git a/desktop/src/main/resources/stylesheets/main.css b/desktop/src/main/resources/stylesheets/main.css deleted file mode 100644 index b3bfdf1..0000000 --- a/desktop/src/main/resources/stylesheets/main.css +++ /dev/null @@ -1,555 +0,0 @@ -body { - margin: 0px; - padding: 0px; - color: #151515; - font-family:sans-serif; - background-color: #f9f7f6; - display: flex; - flex-direction: column; - min-height: 100vh; -} - -a { - outline-color: #F44336; -} - -nav { - display: flex; - background: #e45151; - height: 44px; - width: 100%; - display: flex; - justify-content: center; -} - -nav ul { - display: flex; - padding: 0; - margin: 0; - font-size: 22px; - text-transform: uppercase; - font-weight: bold; - margin: auto; -max-width: 900px; - width: 900px; -} - -nav li { - display: block; - margin-top: auto; - margin-bottom: auto; -} - -nav li a { - padding-left: 8px; - padding-right: 8px; - height: 44px; - line-height: 44px; - display: inline-block; -} - -nav li a:hover { - background-color: #f9f7f6; -} - -nav li .selected:hover { - background-color: #161616; -} - -nav li .selected { - color: #f9f7f6 !important; -} - -#top-border { - height: 60px; - width: 100%; - background: #161616; - display: flex; - justify-content: center; -} - -#top-border > div { - display: flex; - flex-wrap: wrap; - align-items: center; - height: 60px; - margin: auto; -max-width: 900px; - width: 900px; - padding-left: 8px; - padding-right: 8px; -} - -#filler { - flex-grow: 1; -} - -footer { - text-align: center; - padding: 8px; - background: #e45151; - color: #151515; -} - -#content, .grid-container, .masonry { - align-self: center; - width: auto; -} - -#content-wrapper { - display: flex; - flex-flow: row; - justify-content: center; -} - -.grid-container, .masonry { - max-width: 900px; -} -#content { - max-width: 900px; -} - -@media (max-width: 1000px) { - .side-panel { - display: none; - } -} -@media (min-width: 1000px) { - .bottom-panel { - display: none; - } - } - - .bottom-panel { - text-align: center; - background-color: #e4751a; - } - -.bottom-panel a { - display: block; - width: 100%; -} - -.side-panel a { - display: block; - height: 100%; -} - -.title { - font-size: 16px; - padding-left: 10px; - padding-right: 24px; - font-weight: bold; - color: #dbdbdb; - display: inline-flex; - flex: auto; - text-transform: uppercase; -} - -.title span::first-letter { - color: #e45151; -} -.title span { - margin-left: 8px; -} - -a:link, a:visited, a:hover, a:active { - color: #151515; - text-decoration: none; -} - -h1 { - text-align: center; - background: #f0f0f0; - width: max-content; - margin-left: auto; - margin-right: auto; - padding: 4px 8px; - margin-top: 16px; - margin-top: 16px; - margin-bottom: 0px; -} - -.download-icon { - margin-top: 8px; -} - -.logo-icon { - height: 48px; - width: 48px; - background-size: 48px 48px; - display: block; -} - -.server-button { - text-align: center; - margin-top: 12px; - margin-bottom: 12px; -} - -.server-button img { - border: black 2px solid; - max-width: calc(100% - 20px); - width: 500px; -} - -.server-button img:hover { - border-color: #F44336; -} - -.project img { - border-radius: 5px; -} - -.project { -font-size: 12px; - text-align: center; - } - - -.masonry { - -webkit-column-width: 300px; - -moz-column-width: 300px; - column-width: 300px; - -webkit-column-gap: 10px; - -moz-column-gap: 10px; - column-gap: 12px; - margin: 12px; -} - -.grid-container { - display: flex; - justify-content: center; - flex-wrap: wrap; - margin: 6px; - padding: 0; - list-style: none; - width: auto; -} - -.grid-item { - background-color: #eee; - color: #444; - width: 128px; - height: 128px; - text-align: center; - margin: 6px; - min-width: 128px; - flex: 1 1 128px; - max-width: 180px; -} - -.grid-item h2 { - padding: 0; - margin: 0; - margin-left: 14px; - margin-right: 14px; - font-size: 18px; - font-weight: bold; -} - -.grid-item:hover { - background-color: #e45151; -} - -.grid-item div { - width: 100%; - height: 100% -} - -.grid-item i { - width: 40px; - height: 40px; - display: block; - background-size: 40px 40px; - margin-left: auto; - margin-right: auto; - margin-bottom: 4px; - background-repeat: no-repeat; - background-position: bottom; - padding-top: 28px; -} - -.highlight { - color: #F44336; -} - -#search { - background-image: url(/images/icon-search.svg); - background-position: 10px 12px; - background-repeat: no-repeat; - font-size: 16px; - padding: 12px 20px 12px 40px; - border: 2px solid #ddd; - border-radius: 2px; - outline-color: #F44336; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - width: 100%; -} - -#search-wrapper { - width: 100%; - display: inline-block; -} - -#no-results { - display: none; - padding: 24px; - text-align: center; - font-size: 20px; -} - -#commandlist { - list-style-type: none; - padding: 0; - margin: 0; - margin-top: 12px; - border: solid #ddd; - border-width: 0px 0px 1px 0px; -} - -#commandlist a, #commandlist div.headline { - border: solid #ddd; - background-color: #f6f6f6; - padding: 6px; - text-decoration: none; - font-size: 1.4em; - display: block; - border-width: 1px 1px 0 1px; -} - -#commandlist div.headline { - background-color: #e2e2e2; -} - -#commandlist a:hover:not(.headline) { - background-color: #F44336; -} - -#commandlist a:hover:not(.headline) span { - color: #000; -} - -.accordion-button { - background-color: #dfdfdf; - color: #444; - cursor: pointer; - padding: 12px; - font-weight: bold; - font-size: 1.3em; - margin: 0px; - font-family: Sans-serif; -} - -.toggle-all-button { - background-color: #f9f9f9; - color: #444; - cursor: pointer; - padding: 12px; - position: fixed; - bottom: 10px; - right: 10px; - width: 190px; - outline: none; - font-weight: bold; - font-size: 1.3em; - text-align: center; - border: 2px #f9f9f9 solid; -} - -.accordion-button:hover, .toggle-all-button:hover { - background-color: #e45151; -} - -.panel { - padding: 0 18px; - display: flow-root; - background-color: #eee; -} - -.panel h3 { - text-align: left; - margin: 0px; - margin-bottom: 6px; - font-size: 17px; -} - -.panel h3 a { - color: black !important; -} - -.panel a { - font-weight: bold; - color: #F44336; -} - -.subtitle { - text-align: center; - font-size: 20px; -} - -pre { - white-space: pre-wrap; - white-space: -moz-pre-wrap; - white-space: -pre-wrap; - white-space: -o-pre-wrap; - word-wrap: break-word; -} - -.code { - color: #fff; - background-color: #000; - padding: 2px; - display: inline-block; - white-space: normal; - word-break: break-word; - margin-bottom: 5px; - max-width: 550px; - padding: 4px; -} - -.code a:link, .code a:visited { - color: #e45151; - text-decoration: none; - target-new: none; -} - -.code-wrapper { - display: flex; -} - -.code-group { - background-color: #eee; - padding-left: 8px; - padding-right: 8px; - padding-bottom: 4px; - width: 100%; - box-sizing: border-box; - margin: 0; - -webkit-column-break-inside: avoid; - page-break-inside: avoid; - break-inside: avoid-column; - display: inline-grid; - margin-bottom: 15px; -} - -.code-group h2 { - padding-top: 6px; - margin-bottom: 8px; - margin-top: 2px; - font-size: 20px; -} - -.copy-button img { - margin-left: 4px; - margin-top: 2px; - cursor: pointer; -} - -.copy-button { - display: inline-block; - margin-top: auto; - margin-bottom: auto; -} - -.visible { - visibility: visible !important; - opacity: 1; - transition: opacity 0.3s linear; -} - -.hidden { - visibility: hidden !important; - opacity: 0; - transition: visibility 0s 0.3s, opacity 0.3s linear; -} - -.tooltip { - position: fixed; - background: #bbddab; - padding: 4px; - left: 50%; - border: #bbddab 2px solid; - border-radius: 2px; - font-size: 18px; - transform: translateY(-50%) translateX(-50%); - top: 50%; - visibility: hidden; -} - -td:first-child { - white-space: nowrap; - font-weight: bold; - vertical-align: top; -} - -table { - border-spacing: 4px 1px; -} - -@media screen and (max-width: 410px) { - #logo-icon-wrapper { - display: none !important; - } -} - -@media (prefers-color-scheme: dark) { - body { - background-color: #383838; - color: #bebebe; - } - nav a:link,nav a:visited,nav a:hover,nav a:active { - color: #151515; - text-decoration: none; - } - h1 { - background: #282828; - color: #ffffff; - } - a:link, a:visited, a:hover, a:active { - color: #ffffff; - } - .copy-button img, .invert-color { - filter: invert(100%) sepia(0%) saturate(2968%) hue-rotate(17deg) brightness(114%) contrast(87%); - } - .grid-item { - background-color: #1e1e1e; - border-color: #444; - } - #search { - border-color: #444; - background-color: #d3d2d2; - } - #commandlist { - border-color: #444; - } - #commandlist a { - background-color: #282828; - border-color: #444; - } - #commandlist div.headline { - background-color: #585858; - border-color: #444; - } - .panel h3 a { - color: #ffffff !important; - } - .accordion-button, .toggle-all-button { - background-color: #282828; - border-color: #444; - } - .panel { - background-color: #1e1e1e; - } - .toggle-all-button { - color: #ffffff; - } - .code-group { - background: #1e1e1e; - } - .tooltip { - color: #000000; - } -} \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/100.txt b/fastlane/metadata/android/en-US/changelogs/100.txt deleted file mode 100644 index 7aed46c..0000000 --- a/fastlane/metadata/android/en-US/changelogs/100.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add new commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/101.txt b/fastlane/metadata/android/en-US/changelogs/101.txt deleted file mode 100644 index 4565628..0000000 --- a/fastlane/metadata/android/en-US/changelogs/101.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add persistent expand sections toggle -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/102.txt b/fastlane/metadata/android/en-US/changelogs/102.txt deleted file mode 100644 index b4a34cb..0000000 --- a/fastlane/metadata/android/en-US/changelogs/102.txt +++ /dev/null @@ -1 +0,0 @@ -Add commands \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/103.txt b/fastlane/metadata/android/en-US/changelogs/103.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/103.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/104.txt b/fastlane/metadata/android/en-US/changelogs/104.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/104.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/105.txt b/fastlane/metadata/android/en-US/changelogs/105.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/105.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/106.txt b/fastlane/metadata/android/en-US/changelogs/106.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/106.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/107.txt b/fastlane/metadata/android/en-US/changelogs/107.txt deleted file mode 100644 index 242cad9..0000000 --- a/fastlane/metadata/android/en-US/changelogs/107.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add commands -Upgrade sdks -Improve performance \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/108.txt b/fastlane/metadata/android/en-US/changelogs/108.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/108.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/111.txt b/fastlane/metadata/android/en-US/changelogs/111.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/111.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/112.txt b/fastlane/metadata/android/en-US/changelogs/112.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/112.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/113.txt b/fastlane/metadata/android/en-US/changelogs/113.txt deleted file mode 100644 index b4a34cb..0000000 --- a/fastlane/metadata/android/en-US/changelogs/113.txt +++ /dev/null @@ -1 +0,0 @@ -Add commands \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/114.txt b/fastlane/metadata/android/en-US/changelogs/114.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/114.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/115.txt b/fastlane/metadata/android/en-US/changelogs/115.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/115.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/116.txt b/fastlane/metadata/android/en-US/changelogs/116.txt deleted file mode 100644 index 498d551..0000000 --- a/fastlane/metadata/android/en-US/changelogs/116.txt +++ /dev/null @@ -1,3 +0,0 @@ -Fix search bar accessibility issues -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/117.txt b/fastlane/metadata/android/en-US/changelogs/117.txt deleted file mode 100644 index b4a34cb..0000000 --- a/fastlane/metadata/android/en-US/changelogs/117.txt +++ /dev/null @@ -1 +0,0 @@ -Add commands \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/118.txt b/fastlane/metadata/android/en-US/changelogs/118.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/118.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/119.txt b/fastlane/metadata/android/en-US/changelogs/119.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/119.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/120.txt b/fastlane/metadata/android/en-US/changelogs/120.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/120.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/70.txt b/fastlane/metadata/android/en-US/changelogs/70.txt deleted file mode 100644 index 1e812e2..0000000 --- a/fastlane/metadata/android/en-US/changelogs/70.txt +++ /dev/null @@ -1,4 +0,0 @@ -Refactor app to Kotlin Compose -Add new commands -Add new basics -Add new tips \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/71.txt b/fastlane/metadata/android/en-US/changelogs/71.txt deleted file mode 100644 index 45dcfde..0000000 --- a/fastlane/metadata/android/en-US/changelogs/71.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add command description search -Improve navigation performance \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/72.txt b/fastlane/metadata/android/en-US/changelogs/72.txt deleted file mode 100644 index 8e85b6f..0000000 --- a/fastlane/metadata/android/en-US/changelogs/72.txt +++ /dev/null @@ -1,2 +0,0 @@ -Fix tips screen UI width bug -Improve tips screen performance \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/73.txt b/fastlane/metadata/android/en-US/changelogs/73.txt deleted file mode 100644 index 6cd4b6c..0000000 --- a/fastlane/metadata/android/en-US/changelogs/73.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add search priority sorting -Add new commands and update man pages -Upgrade to the latest Jetpack Compose and Kotlin \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/74.txt b/fastlane/metadata/android/en-US/changelogs/74.txt deleted file mode 100644 index 15068bf..0000000 --- a/fastlane/metadata/android/en-US/changelogs/74.txt +++ /dev/null @@ -1,2 +0,0 @@ -Adjust layouts, margins, colors and bottom navigation highlights -Upgrade to the latest Jetpack Compose \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/75.txt b/fastlane/metadata/android/en-US/changelogs/75.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/75.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/76.txt b/fastlane/metadata/android/en-US/changelogs/76.txt deleted file mode 100644 index 5a3206c..0000000 --- a/fastlane/metadata/android/en-US/changelogs/76.txt +++ /dev/null @@ -1 +0,0 @@ -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/77.txt b/fastlane/metadata/android/en-US/changelogs/77.txt deleted file mode 100644 index 4ac4b51..0000000 --- a/fastlane/metadata/android/en-US/changelogs/77.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add commands -Fix cursor tips section -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/79.txt b/fastlane/metadata/android/en-US/changelogs/79.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/79.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/80.txt b/fastlane/metadata/android/en-US/changelogs/80.txt deleted file mode 100644 index c126083..0000000 --- a/fastlane/metadata/android/en-US/changelogs/80.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add monochrome icon -Improve performance -Fix group and command section collapse state not persisting \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/82.txt b/fastlane/metadata/android/en-US/changelogs/82.txt deleted file mode 100644 index ef62631..0000000 --- a/fastlane/metadata/android/en-US/changelogs/82.txt +++ /dev/null @@ -1,2 +0,0 @@ -Fix case sensitive search -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/83.txt b/fastlane/metadata/android/en-US/changelogs/83.txt deleted file mode 100644 index 5a3206c..0000000 --- a/fastlane/metadata/android/en-US/changelogs/83.txt +++ /dev/null @@ -1 +0,0 @@ -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/84.txt b/fastlane/metadata/android/en-US/changelogs/84.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/84.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/85.txt b/fastlane/metadata/android/en-US/changelogs/85.txt deleted file mode 100644 index 929cf2b..0000000 --- a/fastlane/metadata/android/en-US/changelogs/85.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/89.txt b/fastlane/metadata/android/en-US/changelogs/89.txt deleted file mode 100644 index 2c38762..0000000 --- a/fastlane/metadata/android/en-US/changelogs/89.txt +++ /dev/null @@ -1,2 +0,0 @@ -Fix github issues -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/90.txt b/fastlane/metadata/android/en-US/changelogs/90.txt deleted file mode 100644 index 69db8e1..0000000 --- a/fastlane/metadata/android/en-US/changelogs/90.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add commands -Migrate to android sdk 35 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/92.txt b/fastlane/metadata/android/en-US/changelogs/92.txt deleted file mode 100644 index 5a3206c..0000000 --- a/fastlane/metadata/android/en-US/changelogs/92.txt +++ /dev/null @@ -1 +0,0 @@ -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/94.txt b/fastlane/metadata/android/en-US/changelogs/94.txt deleted file mode 100644 index 27a5c62..0000000 --- a/fastlane/metadata/android/en-US/changelogs/94.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add new commands and major update command info data -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/95.txt b/fastlane/metadata/android/en-US/changelogs/95.txt deleted file mode 100644 index 6a43036..0000000 --- a/fastlane/metadata/android/en-US/changelogs/95.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add new commands -Improve dark mode -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/96.txt b/fastlane/metadata/android/en-US/changelogs/96.txt deleted file mode 100644 index 9d06025..0000000 --- a/fastlane/metadata/android/en-US/changelogs/96.txt +++ /dev/null @@ -1,3 +0,0 @@ -Add see also chips -Add new commands -Improve performance \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/97.txt b/fastlane/metadata/android/en-US/changelogs/97.txt deleted file mode 100644 index 60b5133..0000000 --- a/fastlane/metadata/android/en-US/changelogs/97.txt +++ /dev/null @@ -1,2 +0,0 @@ -Improve search with "basics results" -Add new commands \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/98.txt b/fastlane/metadata/android/en-US/changelogs/98.txt deleted file mode 100644 index 9c43127..0000000 --- a/fastlane/metadata/android/en-US/changelogs/98.txt +++ /dev/null @@ -1,3 +0,0 @@ -Fix formatting crash -Add new commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/99.txt b/fastlane/metadata/android/en-US/changelogs/99.txt deleted file mode 100644 index 7aed46c..0000000 --- a/fastlane/metadata/android/en-US/changelogs/99.txt +++ /dev/null @@ -1,2 +0,0 @@ -Add new commands -Upgrade sdks \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt deleted file mode 100644 index 5fdb5ab..0000000 --- a/fastlane/metadata/android/en-US/full_description.txt +++ /dev/null @@ -1,43 +0,0 @@ -The app currently has 7680 manual pages, 22 basic categories and a bunch of general terminal tips. It works 100% offline, doesn't need an internet connection and has no tracking software. - -Categories - -* One-liners -* System information -* System control -* Users & Groups -* Files & Folders -* Input -* Printing -* JSON -* Network -* Search & Find -* GIT -* SSH -* Video & Audio -* Package manager -* Hacking tools -* Terminal games -* Crypto currencies -* VIM Texteditor -* Emacs Texteditor -* Nano Texteditor -* Pico Texteditor -* Micro Texteditor - -Tips - -* Clear and reset the terminal -* List of recent commands -* Close a frozen window/application -* Tab Completion -* Temporary aliases -* Permanent aliases -* Chain commands -* Command syntax -* Cursor navigation -* Redirection -* Special characters in commands -* View file permissions -* Modify file permissions -* Set file permissions via binary references diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png deleted file mode 100644 index e8406fc..0000000 Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png deleted file mode 100644 index e1cffc9..0000000 Binary files a/fastlane/metadata/android/en-US/images/icon.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/01.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/01.png deleted file mode 100644 index 77edb6e..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/01.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/02.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/02.png deleted file mode 100644 index 56a16be..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/02.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/03.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/03.png deleted file mode 100644 index b312557..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/03.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png deleted file mode 100644 index cde4e06..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/04.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/05.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/05.png deleted file mode 100644 index 849ca35..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/05.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/06.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/06.png deleted file mode 100644 index 0ddf464..0000000 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/06.png and /dev/null differ diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt deleted file mode 100644 index fcac99e..0000000 --- a/fastlane/metadata/android/en-US/short_description.txt +++ /dev/null @@ -1 +0,0 @@ -7680 manual pages, 22 basic categories and a bunch of general terminal tips. diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index dac1884..0000000 --- a/gradle.properties +++ /dev/null @@ -1,6 +0,0 @@ -kotlin.code.style=official -android.useAndroidX=true -org.gradle.jvmargs=-Xmx4608m -kotlin.mpp.androidSourceSetLayoutVersion=2 -android.nonTransitiveRClass=false -android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index 559b7ec..0000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,59 +0,0 @@ -[versions] -agp = "8.13.1" -appVersion = "3.4.10" -androidVersionCode = "120" -kotlin = "2.2.21" -accompanistAppcompatTheme = "0.36.0" -activityCompose = "1.11.0" -foundation = "1.9.4" -json = "20250517" -koinCore = "4.1.1" -kotlinxCoroutinesCore = "1.10.2" -kotlinxHtmlJvm = "0.12.0" -lifecycleViewmodelCompose = "2.9.4" -material = "1.9.4" -materialIcons = "1.7.8" -navigationCompose = "2.9.6" -preference = "1.2.1" -sqldelight = "2.2.1" -uiToolingPreview = "1.9.4" -spotless = "8.0.0" -kotlinxCollectionsImmutable = "0.4.0" -benManesVersions = "0.53.0" - - -[libraries] -accompanist-appcompat-theme = { module = "com.google.accompanist:accompanist-appcompat-theme", version.ref = "accompanistAppcompatTheme" } -accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistAppcompatTheme" } -androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } -androidx-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "foundation" } -androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } -androidx-material = { module = "androidx.compose.material:material", version.ref = "material" } -androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core", version.ref = "materialIcons" } -androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } -androidx-preference = { module = "androidx.preference:preference", version.ref = "preference" } -androidx-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "uiToolingPreview" } -androidx-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiToolingPreview" } -androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "uiToolingPreview" } -androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "uiToolingPreview" } -json = { module = "org.json:json", version.ref = "json" } -koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinCore" } -koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koinCore" } -koin-core = { module = "io.insert-koin:koin-core", version.ref = "koinCore" } -kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } -kotlinx-html-jvm = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinxHtmlJvm" } -runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" } -sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } -sqldelight-sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } -kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinxCollectionsImmutable" } - - -[plugins] -android-application = { id = "com.android.application", version.ref = "agp" } -android-library = { id = "com.android.library", version.ref = "agp" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } -compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } -ben-manes-versions = { id = "com.github.ben-manes.versions", version.ref = "benManesVersions" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 63e0e83..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 1b6c787..0000000 --- a/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index ac1b06f..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/proguard-rules.pro b/proguard-rules.pro deleted file mode 100644 index 3b46cd6..0000000 --- a/proguard-rules.pro +++ /dev/null @@ -1,2 +0,0 @@ -# VerifyError: Superclass androidx.core.app.f of androidx.activity.ComponentActivity is declared final / https://issuetracker.google.com/issues/237785592 --keep class androidx.core.app.** { *; } \ No newline at end of file diff --git a/pull_screenshots.sh b/pull_screenshots.sh deleted file mode 100644 index 4a223e3..0000000 --- a/pull_screenshots.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -adb root -adb pull /data/data/com.inspiredandroid.linuxcommandbibliotheca/files/ . -echo "Move files from device to project" -mv files/*.png art -echo "Move files to art folder" -rmdir files -echo "Move files to fastlane folder" -cp art/screen-1.png fastlane/metadata/android/en-US/images/phoneScreenshots/01.png -cp art/screen-2-dark.png fastlane/metadata/android/en-US/images/phoneScreenshots/02.png -cp art/screen-3.png fastlane/metadata/android/en-US/images/phoneScreenshots/03.png -cp art/screen-4-dark.png fastlane/metadata/android/en-US/images/phoneScreenshots/04.png -cp art/screen-1-tablet.png fastlane/metadata/android/en-US/images/phoneScreenshots/05.png -cp art/screen-2-tablet-dark.png fastlane/metadata/android/en-US/images/phoneScreenshots/06.png \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 5461fdd..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -pluginManagement { - repositories { - google() - gradlePluginPortal() - mavenCentral() - } -} - -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) // Enforce centralized repositories - repositories { - google() - mavenCentral() - } -} - -rootProject.name = "Linux Command Library" - -include(":android", ":common", ":desktop", ":cli")
" - } - if (it.data1.isNotBlank()) { - b { - text(it.data1) - } - } - unsafe { - +"" - } - if (it.extra.isNotBlank()) { - code(it.data2.replace("\\n", "
"), it.extra) - } else { - text(it.data2) - } - unsafe { - +"