├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── assets └── finch.gif ├── build.gradle ├── common-loggers ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── common │ └── loggers │ ├── FinchLogger.kt │ ├── FinchNetworkLogger.kt │ ├── data │ └── models │ │ ├── HeaderHttpModel.kt │ │ ├── MediaType.kt │ │ └── NetworkLogEntity.kt │ └── utils │ ├── FormatUtil.kt │ └── GsonUtil.kt ├── common ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ ├── common │ ├── contracts │ │ ├── Finch.kt │ │ ├── FinchListItem.kt │ │ └── component │ │ │ ├── Cell.kt │ │ │ ├── Component.kt │ │ │ ├── ExpandableComponent.kt │ │ │ ├── ValueWrapperComponent.kt │ │ │ └── ViewHolder.kt │ ├── listeners │ │ ├── LogListener.kt │ │ ├── NetworkLogListener.kt │ │ ├── OverlayListener.kt │ │ ├── UpdateListener.kt │ │ └── VisibilityListener.kt │ └── models │ │ ├── Configuration.kt │ │ ├── Inset.kt │ │ ├── Position.kt │ │ └── Text.kt │ └── components │ ├── CheckBox.kt │ ├── Divider.kt │ ├── ItemList.kt │ ├── KeyValueList.kt │ ├── Label.kt │ ├── LongText.kt │ ├── MultipleSelectionList.kt │ ├── Padding.kt │ ├── ProgressBar.kt │ ├── SingleSelectionList.kt │ ├── Slider.kt │ ├── Switch.kt │ ├── TextInput.kt │ └── special │ ├── AnimationSpeed.kt │ ├── AppInfo.kt │ ├── DesignOverlay.kt │ ├── DeveloperOptions.kt │ ├── DeviceInfo.kt │ ├── ForceCrash.kt │ ├── Gallery.kt │ ├── Header.kt │ ├── LifecycleLogs.kt │ ├── Logs.kt │ ├── LoremIpsumGenerator.kt │ ├── NetworkLogs.kt │ ├── ScreenCaptureToolbox.kt │ ├── ScreenRecording.kt │ └── Screenshot.kt ├── config └── detekt │ └── config.yml ├── core ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── kernel │ │ └── finch │ │ ├── FinchCore.kt │ │ └── core │ │ ├── FinchImpl.kt │ │ ├── OverlayFragment.kt │ │ ├── data │ │ ├── db │ │ │ ├── FinchDatabase.kt │ │ │ └── NetworkLogDao.kt │ │ └── models │ │ │ └── Period.kt │ │ ├── list │ │ ├── CellAdapter.kt │ │ ├── cells │ │ │ ├── ButtonCell.kt │ │ │ ├── CheckBoxCell.kt │ │ │ ├── DividerCell.kt │ │ │ ├── ExpandableHeaderCell.kt │ │ │ ├── ExpandedItemCheckBoxCell.kt │ │ │ ├── ExpandedItemKeyValueCell.kt │ │ │ ├── ExpandedItemRadioButtonCell.kt │ │ │ ├── ExpandedItemTextCell.kt │ │ │ ├── HeaderCell.kt │ │ │ ├── PaddingCell.kt │ │ │ ├── ProgressBarCell.kt │ │ │ ├── SliderCell.kt │ │ │ ├── SwitchCell.kt │ │ │ └── TextCell.kt │ │ └── delegates │ │ │ ├── AnimationSpeedDelegate.kt │ │ │ ├── AppInfoDelegate.kt │ │ │ ├── CheckBoxDelegate.kt │ │ │ ├── DesignOverlayDelegate.kt │ │ │ ├── DeveloperOptionsDelegate.kt │ │ │ ├── DeviceInfoDelegate.kt │ │ │ ├── DividerDelegate.kt │ │ │ ├── ForceCrashDelegate.kt │ │ │ ├── GalleryDelegate.kt │ │ │ ├── HeaderDelegate.kt │ │ │ ├── ItemListDelegate.kt │ │ │ ├── KeyValueListDelegate.kt │ │ │ ├── LabelDelegate.kt │ │ │ ├── LifecycleLogsDelegate.kt │ │ │ ├── LogsDelegate.kt │ │ │ ├── LongTextDelegate.kt │ │ │ ├── LoremIpsumGeneratorDelegate.kt │ │ │ ├── MultipleSelectionListDelegate.kt │ │ │ ├── NetworkLogsDelegate.kt │ │ │ ├── PaddingDelegate.kt │ │ │ ├── ProgressBarDelegate.kt │ │ │ ├── ScreenCaptureToolboxDelegate.kt │ │ │ ├── ScreenRecordingDelegate.kt │ │ │ ├── ScreenshotDelegate.kt │ │ │ ├── SingleSelectionListDelegate.kt │ │ │ ├── SliderDelegate.kt │ │ │ ├── SwitchDelegate.kt │ │ │ ├── TextInputDelegate.kt │ │ │ └── shared │ │ │ ├── ExpandableComponentDelegate.kt │ │ │ └── ValueWrapperComponentDelegate.kt │ │ ├── manager │ │ ├── DebugMenuInjector.kt │ │ ├── LifecycleLogManager.kt │ │ ├── ListManager.kt │ │ ├── LocalStorageManager.kt │ │ ├── LogManager.kt │ │ ├── MemoryStorageManager.kt │ │ ├── NetworkLogManager.kt │ │ ├── NotificationManager.kt │ │ ├── RetentionManager.kt │ │ ├── ScreenCaptureManager.kt │ │ ├── ShakeDetectorManager.kt │ │ ├── UiManager.kt │ │ └── listener │ │ │ ├── BaseListenerManager.kt │ │ │ ├── LogListenerManager.kt │ │ │ ├── NetworkLogListenerManager.kt │ │ │ ├── OverlayListenerManager.kt │ │ │ ├── UpdateListenerManager.kt │ │ │ └── VisibilityListenerManager.kt │ │ ├── presentation │ │ ├── ChildHorizontalScrollView.kt │ │ ├── InternalDebugMenuView.kt │ │ ├── OverlayFrameLayout.kt │ │ ├── TolerantScrollView.kt │ │ ├── detail │ │ │ ├── log │ │ │ │ └── dialog │ │ │ │ │ ├── LogDetailDialogFragment.kt │ │ │ │ │ └── LogDetailDialogViewModel.kt │ │ │ └── networklog │ │ │ │ ├── BaseFinchActivity.kt │ │ │ │ ├── ClearNetworkLogService.kt │ │ │ │ ├── ContainerActivity.kt │ │ │ │ ├── NetworkLogActivity.kt │ │ │ │ ├── NetworkLogFragment.kt │ │ │ │ ├── NetworkLogListFragment.kt │ │ │ │ ├── NetworkLogOverviewFragment.kt │ │ │ │ ├── NetworkLogPayloadFragment.kt │ │ │ │ └── list │ │ │ │ ├── NetworkLogAdapter.kt │ │ │ │ └── NetworkLogViewHolder.kt │ │ └── gallery │ │ │ ├── DeleteConfirmationDialogFragment.kt │ │ │ ├── GalleryActivity.kt │ │ │ ├── GalleryViewModel.kt │ │ │ ├── MediaPreviewDialogFragment.kt │ │ │ └── list │ │ │ ├── GalleryAdapter.kt │ │ │ ├── GalleryListItem.kt │ │ │ ├── ImageViewHolder.kt │ │ │ ├── SectionHeaderViewHolder.kt │ │ │ └── VideoViewHolder.kt │ │ ├── provider │ │ └── FinchFileProvider.kt │ │ └── util │ │ ├── FileUtil.kt │ │ ├── FinchUtil.kt │ │ ├── FormatterNetworkLog.kt │ │ ├── Helpers.kt │ │ ├── LifecycleLogEntry.kt │ │ ├── LogEntry.kt │ │ ├── ScreenCaptureService.kt │ │ ├── ScreenshotWriter.kt │ │ ├── SimpleActivityLifecycleCallbacks.kt │ │ ├── Text.kt │ │ └── extension │ │ ├── Activity.kt │ │ ├── CharSequence.kt │ │ ├── Context.kt │ │ ├── TextView.kt │ │ ├── View.kt │ │ └── ViewModelStoreOwner.kt │ └── res │ ├── anim │ ├── finch_item_animation_fall_down.xml │ └── finch_layout_animation_list.xml │ ├── drawable │ ├── finch_bg_gradient.xml │ ├── finch_ic_bullet_point.xml │ ├── finch_ic_close.xml │ ├── finch_ic_collapse.xml │ ├── finch_ic_delete.xml │ ├── finch_ic_empty.xml │ ├── finch_ic_expand.xml │ ├── finch_ic_lock.xml │ ├── finch_ic_notification.xml │ ├── finch_ic_recording.xml │ ├── finch_ic_search.xml │ ├── finch_ic_share.xml │ ├── finch_ic_toggle_details_off.xml │ └── finch_ic_toggle_details_on.xml │ ├── font │ ├── finch_console.xml │ └── finch_console_font.ttf │ ├── layout │ ├── finch_activity_container.xml │ ├── finch_activity_gallery.xml │ ├── finch_activity_network_log.xml │ ├── finch_cell_button.xml │ ├── finch_cell_check_box.xml │ ├── finch_cell_divider.xml │ ├── finch_cell_expandable_header.xml │ ├── finch_cell_expanded_item_check_box.xml │ ├── finch_cell_expanded_item_radio_button.xml │ ├── finch_cell_expanded_item_text.xml │ ├── finch_cell_header.xml │ ├── finch_cell_padding.xml │ ├── finch_cell_progress_bar.xml │ ├── finch_cell_slider.xml │ ├── finch_cell_switch.xml │ ├── finch_cell_text.xml │ ├── finch_dialog_fragment_log_detail.xml │ ├── finch_dialog_fragment_log_detail_scrolling.xml │ ├── finch_dialog_fragment_media_preview.xml │ ├── finch_fragment_network_log_list.xml │ ├── finch_fragment_network_log_overview.xml │ ├── finch_fragment_network_log_payload.xml │ ├── finch_item_gallery_image.xml │ ├── finch_item_gallery_section_header.xml │ ├── finch_item_gallery_video.xml │ ├── finch_item_network_log.xml │ └── finch_view_text_input_dialog.xml │ ├── menu │ ├── finch_gallery.xml │ ├── finch_log_detail.xml │ ├── finch_main.xml │ └── finch_network_log.xml │ ├── values-sw720dp │ └── dimens.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── finch_provider_paths.xml ├── dependencies.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── log-grpc-noop ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── networklog │ └── grpc │ └── FinchGrpcLogger.kt ├── log-grpc ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── networklog │ └── grpc │ ├── FinchClientInterceptor.kt │ ├── FinchGrpcLogger.kt │ └── FinchGrpcLoggerImpl.kt ├── log-noop ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── log │ └── FinchLogger.kt ├── log-okhttp-noop ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── networklog │ └── okhttp │ └── FinchOkHttpLogger.kt ├── log-okhttp ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── networklog │ └── okhttp │ ├── FinchInterceptor.kt │ ├── FinchOkHttpLogger.kt │ └── FinchOkHttpLoggerImpl.kt ├── log ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ └── log │ ├── FinchLogger.kt │ ├── LoggerImpl.kt │ └── utils │ ├── Log.kt │ └── extensions │ └── Log.kt ├── noop ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ ├── DebugMenuView.kt │ └── Finch.kt ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── kernel │ │ └── finch │ │ └── sample │ │ ├── ApiService.kt │ │ ├── App.kt │ │ └── MainActivity.kt │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── settings.gradle ├── ui-activity ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── kernel │ │ └── finch │ │ ├── Finch.kt │ │ └── implementation │ │ ├── ActivityUiManager.kt │ │ └── DebugMenuActivity.kt │ └── res │ └── layout │ └── finch_activity_debug_menu.xml ├── ui-bottom-sheet ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── kernel │ │ └── finch │ │ ├── Finch.kt │ │ └── implementation │ │ ├── BottomSheetUiManager.kt │ │ └── DebugMenuBottomSheet.kt │ └── res │ └── values │ └── dimens.xml ├── ui-dialog ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ ├── Finch.kt │ └── implementation │ ├── DebugMenuDialog.kt │ └── DialogUiManager.kt ├── ui-drawer ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── kernel │ │ └── finch │ │ ├── Finch.kt │ │ └── implementation │ │ ├── DebugMenuDrawerLayout.kt │ │ └── DrawerUiManager.kt │ └── res │ └── values │ └── dimens.xml ├── ui-view ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── kernel │ └── finch │ ├── DebugMenuView.kt │ └── Finch.kt └── utils ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml └── java └── com └── kernel └── finch └── utils ├── Helpers.kt ├── extensions ├── Bundle.kt ├── Context.kt └── View.kt └── view └── GestureBlockingRecyclerView.kt /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | insert_final_newline = true 6 | 7 | [*.{yml, json}] 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.{kt, kts, java}] 12 | indent_size = 4 13 | max_line_length = 100 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /.idea 3 | .gradle 4 | /local.properties 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /assets/finch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/assets/finch.gif -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply from: 'dependencies.gradle' 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.6.21' 5 | repositories { 6 | mavenCentral() 7 | google() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:4.0.2' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /common-loggers/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /common-loggers/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.library" 2 | apply plugin: "kotlin-android" 3 | 4 | android { 5 | compileSdkVersion versions.compileSdk 6 | defaultConfig { 7 | minSdkVersion versions.minSdk 8 | targetSdkVersion versions.targetSdk 9 | versionCode versions.libraryVersionCode 10 | versionName versions.libraryVersion 11 | consumerProguardFiles "consumer-rules.pro" 12 | } 13 | } 14 | 15 | dependencies { 16 | implementation fileTree(dir: 'libs', include: ['*.jar']) 17 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" 18 | 19 | implementation "androidx.room:room-runtime:$versions.room" 20 | implementation "com.google.code.gson:gson:$versions.gson" 21 | } 22 | 23 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { 24 | kotlinOptions { 25 | jvmTarget = "1.8" 26 | freeCompilerArgs = ["-Xjvm-default=enable"] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common-loggers/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/common-loggers/consumer-rules.pro -------------------------------------------------------------------------------- /common-loggers/proguard-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/common-loggers/proguard-rules.pro -------------------------------------------------------------------------------- /common-loggers/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /common-loggers/src/main/java/com/kernel/finch/common/loggers/FinchLogger.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.loggers 2 | 3 | interface FinchLogger { 4 | 5 | fun log(message: CharSequence, label: String? = null, payload: CharSequence? = null) = Unit 6 | 7 | fun clearLogs(label: String? = null) = Unit 8 | 9 | fun register( 10 | onNewLog: (message: CharSequence, label: String?, payload: CharSequence?) -> Unit, 11 | clearLogs: (label: String?) -> Unit 12 | ) = Unit 13 | } 14 | -------------------------------------------------------------------------------- /common-loggers/src/main/java/com/kernel/finch/common/loggers/FinchNetworkLogger.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.loggers 2 | 3 | import com.kernel.finch.common.loggers.data.models.NetworkLogEntity 4 | 5 | interface FinchNetworkLogger { 6 | 7 | val logger: Any? get() = null 8 | 9 | fun logNetworkEvent(networkLog: NetworkLogEntity) = Unit 10 | 11 | fun clearNetworkLogs() = Unit 12 | 13 | fun register( 14 | onNewLog: (networkLog: NetworkLogEntity) -> Unit, 15 | clearLogs: () -> Unit 16 | ) = Unit 17 | } 18 | -------------------------------------------------------------------------------- /common-loggers/src/main/java/com/kernel/finch/common/loggers/data/models/HeaderHttpModel.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.loggers.data.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | @Keep 6 | data class HeaderHttpModel( 7 | val name: String, 8 | val value: String 9 | ) 10 | -------------------------------------------------------------------------------- /common-loggers/src/main/java/com/kernel/finch/common/loggers/data/models/MediaType.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.loggers.data.models 2 | 3 | object MediaType { 4 | const val APPLICATION_XML = "application/xml" 5 | const val APPLICATION_JSON = "application/json" 6 | const val APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded" 7 | 8 | const val TEXT_HTML = "text/html" 9 | const val TEXT_PLAIN = "text/plain" 10 | 11 | const val WILDCARD = "*/*" 12 | } -------------------------------------------------------------------------------- /common-loggers/src/main/java/com/kernel/finch/common/loggers/utils/GsonUtil.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.loggers.utils 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.GsonBuilder 5 | 6 | object GsonUtil { 7 | 8 | private val gson: Gson by lazy { 9 | GsonBuilder() 10 | .disableHtmlEscaping() 11 | .setPrettyPrinting() 12 | .create() 13 | } 14 | 15 | val instance: Gson 16 | get() { 17 | return gson.newBuilder() 18 | .serializeNulls() 19 | .create() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /common/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.library" 2 | apply plugin: "kotlin-android" 3 | apply plugin: "kotlin-android-extensions" 4 | 5 | android { 6 | compileSdkVersion versions.compileSdk 7 | defaultConfig { 8 | minSdkVersion versions.minSdk 9 | targetSdkVersion versions.targetSdk 10 | versionCode versions.libraryVersionCode 11 | versionName versions.libraryVersion 12 | consumerProguardFiles "consumer-rules.pro" 13 | } 14 | } 15 | 16 | dependencies { 17 | implementation fileTree(dir: 'libs', include: ['*.jar']) 18 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" 19 | implementation "androidx.annotation:annotation:$versions.annotations" 20 | implementation "androidx.appcompat:appcompat:$versions.appCompat" 21 | implementation "androidx.recyclerview:recyclerview:$versions.recyclerView" 22 | api project(":common-loggers") 23 | } 24 | 25 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { 26 | kotlinOptions { 27 | jvmTarget = "1.8" 28 | freeCompilerArgs = ["-Xjvm-default=enable"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/common/consumer-rules.pro -------------------------------------------------------------------------------- /common/proguard-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/common/proguard-rules.pro -------------------------------------------------------------------------------- /common/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/contracts/FinchListItem.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.contracts 2 | 3 | import com.kernel.finch.common.models.Text 4 | 5 | interface FinchListItem { 6 | 7 | val title: Text 8 | 9 | val id: String 10 | get() = when (val title = title) { 11 | is Text.CharSequence -> title.charSequence.toString() 12 | is Text.ResourceId -> title.resId.toString() 13 | } 14 | 15 | override fun equals(other: Any?): Boolean 16 | 17 | override fun hashCode(): Int 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/contracts/component/Cell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.contracts.component 2 | 3 | interface Cell> { 4 | 5 | val id: String 6 | 7 | fun createViewHolderDelegate(): ViewHolder.Delegate 8 | 9 | override fun equals(other: Any?): Boolean 10 | 11 | override fun hashCode(): Int 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/contracts/component/Component.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.contracts.component 2 | 3 | import androidx.annotation.RestrictTo 4 | import java.util.* 5 | 6 | interface Component> { 7 | 8 | val id: String 9 | 10 | fun createComponentDelegate(): Delegate = 11 | throw IllegalStateException("Built-in Components should never create their own Delegates.") 12 | 13 | override fun equals(other: Any?): Boolean 14 | 15 | override fun hashCode(): Int 16 | 17 | interface Delegate> { 18 | 19 | fun createCells(component: C): List> 20 | 21 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 22 | @Suppress("UNCHECKED_CAST") 23 | fun forceCreateCells(component: Component<*>) = createCells(component as C) 24 | } 25 | 26 | companion object { 27 | val randomId get() = UUID.randomUUID().toString() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/contracts/component/ExpandableComponent.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.contracts.component 2 | 3 | import com.kernel.finch.common.contracts.Finch 4 | import com.kernel.finch.common.models.Text 5 | 6 | interface ExpandableComponent> : Component { 7 | 8 | val isExpandedInitially: Boolean 9 | 10 | fun getHeaderTitle(finch: Finch): Text 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/contracts/component/ViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.contracts.component 2 | 3 | import android.view.View 4 | import android.view.ViewGroup 5 | import androidx.annotation.RestrictTo 6 | import androidx.recyclerview.widget.RecyclerView 7 | 8 | abstract class ViewHolder>(view: View) : RecyclerView.ViewHolder(view) { 9 | 10 | abstract fun bind(model: T) 11 | 12 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 13 | @Suppress("UNCHECKED_CAST") 14 | fun forceBind(model: Cell<*>) = try { 15 | bind(model as T) 16 | } catch (_: ClassCastException) { 17 | } 18 | 19 | abstract class Delegate> { 20 | 21 | abstract fun createViewHolder(parent: ViewGroup): ViewHolder 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/listeners/LogListener.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.listeners 2 | 3 | interface LogListener { 4 | fun onAdded(tag: String?, message: CharSequence, payload: CharSequence?) 5 | } 6 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/listeners/NetworkLogListener.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.listeners 2 | 3 | import com.kernel.finch.common.loggers.data.models.NetworkLogEntity 4 | 5 | interface NetworkLogListener { 6 | fun onAdded(networkLog: NetworkLogEntity) 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/listeners/OverlayListener.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.listeners 2 | 3 | import android.graphics.Canvas 4 | import com.kernel.finch.common.models.Inset 5 | 6 | interface OverlayListener { 7 | fun onDrawOver(canvas: Canvas, insets: Inset) 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/listeners/UpdateListener.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.listeners 2 | 3 | interface UpdateListener { 4 | fun onContentsChanged() 5 | fun onAllPendingChangesApplied() = Unit 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/listeners/VisibilityListener.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.listeners 2 | 3 | interface VisibilityListener { 4 | fun onShown() = Unit 5 | fun onHidden() = Unit 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/models/Inset.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.models 2 | 3 | data class Inset( 4 | val left: Int = 0, 5 | val top: Int = 0, 6 | val right: Int = 0, 7 | val bottom: Int = 0 8 | ) 9 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/models/Position.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.models 2 | 3 | sealed class Position { 4 | 5 | object Bottom : Position() 6 | 7 | object Top : Position() 8 | 9 | class Below(val id: String) : Position() 10 | 11 | class Above(val id: String) : Position() 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/common/models/Text.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.common.models 2 | 3 | import androidx.annotation.StringRes 4 | 5 | sealed class Text { 6 | 7 | data class CharSequence(val charSequence: kotlin.CharSequence) : Text() 8 | 9 | data class ResourceId(@StringRes val resId: Int) : Text() 10 | 11 | var suffix: kotlin.CharSequence = "" 12 | private set 13 | 14 | fun with(suffix: kotlin.CharSequence) = this.also { 15 | this.suffix = suffix 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/Divider.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components 2 | 3 | import com.kernel.finch.common.contracts.component.Component 4 | 5 | data class Divider( 6 | override val id: String = Component.randomId 7 | ) : Component 8 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/KeyValueList.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components 2 | 3 | import androidx.annotation.StringRes 4 | import com.kernel.finch.common.contracts.Finch 5 | import com.kernel.finch.common.contracts.component.Component 6 | import com.kernel.finch.common.contracts.component.ExpandableComponent 7 | import com.kernel.finch.common.models.Text 8 | 9 | @Suppress("Unused") 10 | data class KeyValueList( 11 | val title: Text, 12 | val pairs: List>, 13 | override val isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY, 14 | override val id: String = Component.randomId 15 | ) : ExpandableComponent { 16 | 17 | constructor( 18 | title: CharSequence, 19 | pairs: List>, 20 | isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY, 21 | id: String = Component.randomId 22 | ) : this( 23 | title = Text.CharSequence(title), 24 | pairs = pairs.map { Text.CharSequence(it.first) to Text.CharSequence(it.second) }, 25 | isExpandedInitially = isExpandedInitially, 26 | id = id 27 | ) 28 | 29 | constructor( 30 | @StringRes title: Int, 31 | pairs: List>, 32 | isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY, 33 | id: String = Component.randomId 34 | ) : this( 35 | title = Text.ResourceId(title), 36 | pairs = pairs.map { Text.CharSequence(it.first) to Text.CharSequence(it.second) }, 37 | isExpandedInitially = isExpandedInitially, 38 | id = id 39 | ) 40 | 41 | override fun getHeaderTitle(finch: Finch) = title 42 | 43 | companion object { 44 | private const val DEFAULT_IS_EXPANDED_INITIALLY = false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/LongText.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components 2 | 3 | import androidx.annotation.StringRes 4 | import com.kernel.finch.common.contracts.Finch 5 | import com.kernel.finch.common.contracts.component.Component 6 | import com.kernel.finch.common.contracts.component.ExpandableComponent 7 | import com.kernel.finch.common.models.Text 8 | 9 | @Suppress("Unused") 10 | data class LongText( 11 | val title: Text, 12 | val text: Text, 13 | override val isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY, 14 | override val id: String = Component.randomId 15 | ) : ExpandableComponent { 16 | 17 | constructor( 18 | title: CharSequence, 19 | text: CharSequence, 20 | isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY, 21 | id: String = Component.randomId 22 | ) : this( 23 | title = Text.CharSequence(title), 24 | text = Text.CharSequence(text), 25 | isExpandedInitially = isExpandedInitially, 26 | id = id 27 | ) 28 | 29 | constructor( 30 | @StringRes title: Int, 31 | @StringRes text: Int, 32 | isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY, 33 | id: String = Component.randomId 34 | ) : this( 35 | title = Text.ResourceId(title), 36 | text = Text.ResourceId(text), 37 | isExpandedInitially = isExpandedInitially, 38 | id = id 39 | ) 40 | 41 | override fun getHeaderTitle(finch: Finch) = title 42 | 43 | companion object { 44 | private const val DEFAULT_IS_EXPANDED_INITIALLY = false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/Padding.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components 2 | 3 | import com.kernel.finch.common.contracts.component.Component 4 | 5 | data class Padding( 6 | override val id: String = Component.randomId 7 | ) : Component 8 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/ProgressBar.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components 2 | 3 | import com.kernel.finch.common.contracts.component.Component 4 | 5 | data class ProgressBar( 6 | override val id: String = Component.randomId 7 | ) : Component 8 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/AnimationSpeed.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import com.kernel.finch.common.contracts.component.ValueWrapperComponent 4 | import com.kernel.finch.common.models.Text 5 | 6 | data class AnimationSpeed( 7 | override val text: (Boolean) -> Text = { Text.CharSequence(DEFAULT_TEXT) }, 8 | val multiplier: Float = DEFAULT_MULTIPLIER, 9 | override val initialValue: Boolean = DEFAULT_INITIAL_VALUE, 10 | override val isEnabled: Boolean = DEFAULT_IS_ENABLED, 11 | override val isValuePersisted: Boolean = DEFAULT_IS_VALUE_PERSISTED, 12 | override val onValueChanged: (Boolean) -> Unit = DEFAULT_ON_VALUE_CHANGED 13 | ) : ValueWrapperComponent { 14 | 15 | override val id: String = ID 16 | override val shouldRequireConfirmation = false 17 | 18 | companion object { 19 | const val ID = "animationSpeed" 20 | private const val DEFAULT_TEXT = "Slow animations" 21 | private const val DEFAULT_MULTIPLIER = 4f 22 | private const val DEFAULT_INITIAL_VALUE = false 23 | private const val DEFAULT_IS_ENABLED = true 24 | private const val DEFAULT_IS_VALUE_PERSISTED = false 25 | private val DEFAULT_ON_VALUE_CHANGED: (Boolean) -> Unit = {} 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/AppInfo.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class AppInfo( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val shouldOpenInNewTask: Boolean = DEFAULT_SHOULD_OPEN_IN_NEW_TASK, 11 | val type: Label.Type = DEFAULT_TYPE, 12 | @DrawableRes val icon: Int? = DEFAULT_ICON, 13 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 14 | val onButtonPressed: () -> Unit = DEFAULT_ON_BUTTON_PRESSED 15 | ) : Component { 16 | 17 | override val id: String = ID 18 | 19 | companion object { 20 | const val ID = "appInfo" 21 | private const val DEFAULT_TEXT = "App info" 22 | private const val DEFAULT_SHOULD_OPEN_IN_NEW_TASK = false 23 | private val DEFAULT_TYPE = Label.Type.BUTTON 24 | private val DEFAULT_ICON: Int? = null 25 | private const val DEFAULT_IS_ENABLED = true 26 | private val DEFAULT_ON_BUTTON_PRESSED: () -> Unit = {} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/DesignOverlay.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.ColorInt 4 | import androidx.annotation.Dimension 5 | import com.kernel.finch.common.contracts.component.ValueWrapperComponent 6 | import com.kernel.finch.common.models.Inset 7 | import com.kernel.finch.common.models.Text 8 | 9 | data class DesignOverlay( 10 | override val text: (Boolean) -> Text = { Text.CharSequence(DEFAULT_TEXT) }, 11 | @Dimension val grid: Int? = DEFAULT_GRID, 12 | @Dimension val overlayPrimary: Int? = DEFAULT_OVERLAY_PRIMARY, 13 | @Dimension val overlaySecondary: Int? = DEFAULT_OVERLAY_SECONDARY, 14 | @ColorInt val color: Int? = DEFAULT_COLOR, 15 | override val initialValue: Boolean = DEFAULT_INITIAL_VALUE, 16 | override val isEnabled: Boolean = DEFAULT_IS_ENABLED, 17 | override val isValuePersisted: Boolean = DEFAULT_IS_VALUE_PERSISTED, 18 | val applyInsets: ((windowInsets: Inset) -> Inset)? = DEFAULT_APPLY_INSETS, 19 | override val onValueChanged: (Boolean) -> Unit = DEFAULT_ON_VALUE_CHANGED 20 | ) : ValueWrapperComponent { 21 | 22 | override val id: String = ID 23 | override val shouldRequireConfirmation = false 24 | 25 | companion object { 26 | const val ID = "designOverlay" 27 | private const val DEFAULT_TEXT = "Design overlay" 28 | private val DEFAULT_GRID: Int? = null 29 | private val DEFAULT_OVERLAY_PRIMARY: Int? = null 30 | private val DEFAULT_OVERLAY_SECONDARY: Int? = null 31 | private val DEFAULT_COLOR: Int? = null 32 | private const val DEFAULT_INITIAL_VALUE = false 33 | private const val DEFAULT_IS_ENABLED = true 34 | private const val DEFAULT_IS_VALUE_PERSISTED = false 35 | private val DEFAULT_APPLY_INSETS: ((windowInsets: Inset) -> Inset)? = null 36 | private val DEFAULT_ON_VALUE_CHANGED: (Boolean) -> Unit = {} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/DeveloperOptions.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class DeveloperOptions( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val shouldOpenInNewTask: Boolean = DEFAULT_SHOULD_OPEN_IN_NEW_TASK, 11 | val type: Label.Type = DEFAULT_TYPE, 12 | @DrawableRes val icon: Int? = DEFAULT_ICON, 13 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 14 | val onButtonPressed: () -> Unit = DEFAULT_ON_BUTTON_PRESSED 15 | ) : Component { 16 | 17 | override val id: String = ID 18 | 19 | companion object { 20 | const val ID = "developerOptions" 21 | private const val DEFAULT_TEXT = "Developer options" 22 | private const val DEFAULT_SHOULD_OPEN_IN_NEW_TASK = false 23 | private val DEFAULT_TYPE = Label.Type.BUTTON 24 | private val DEFAULT_ICON: Int? = null 25 | private const val DEFAULT_IS_ENABLED = true 26 | private val DEFAULT_ON_BUTTON_PRESSED: () -> Unit = {} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/DeviceInfo.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import com.kernel.finch.common.contracts.Finch 4 | import com.kernel.finch.common.contracts.component.ExpandableComponent 5 | import com.kernel.finch.common.models.Text 6 | 7 | data class DeviceInfo( 8 | val title: Text = Text.CharSequence(DEFAULT_TITLE), 9 | val shouldShowManufacturer: Boolean = DEFAULT_SHOULD_SHOW_MANUFACTURER, 10 | val shouldShowModel: Boolean = DEFAULT_SHOULD_SHOW_MODEL, 11 | val shouldShowResolutionsPx: Boolean = DEFAULT_SHOULD_SHOW_RESOLUTION_PX, 12 | val shouldShowResolutionsDp: Boolean = DEFAULT_SHOULD_SHOW_RESOLUTION_DP, 13 | val shouldShowDensity: Boolean = DEFAULT_SHOULD_SHOW_DENSITY, 14 | val shouldShowAndroidVersion: Boolean = DEFAULT_SHOULD_SHOW_ANDROID_VERSION, 15 | override val isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY 16 | ) : ExpandableComponent { 17 | 18 | override val id: String = ID 19 | 20 | override fun getHeaderTitle(finch: Finch) = title 21 | 22 | companion object { 23 | const val ID = "deviceInfo" 24 | private const val DEFAULT_TITLE = "Device info" 25 | private const val DEFAULT_SHOULD_SHOW_MANUFACTURER = true 26 | private const val DEFAULT_SHOULD_SHOW_MODEL = true 27 | private const val DEFAULT_SHOULD_SHOW_RESOLUTION_PX = true 28 | private const val DEFAULT_SHOULD_SHOW_RESOLUTION_DP = true 29 | private const val DEFAULT_SHOULD_SHOW_DENSITY = true 30 | private const val DEFAULT_SHOULD_SHOW_ANDROID_VERSION = true 31 | private const val DEFAULT_IS_EXPANDED_INITIALLY = false 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/ForceCrash.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class ForceCrash( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val exception: RuntimeException = RuntimeException(DEFAULT_EXCEPTION_MESSAGE), 11 | val type: Label.Type = DEFAULT_TYPE, 12 | @DrawableRes val icon: Int? = DEFAULT_ICON, 13 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 14 | val onButtonPressed: () -> Unit = DEFAULT_ON_BUTTON_PRESSED 15 | ) : Component { 16 | 17 | override val id: String = ID 18 | 19 | companion object { 20 | const val ID = "forceCrash" 21 | private const val DEFAULT_TEXT = "Force crash" 22 | private const val DEFAULT_EXCEPTION_MESSAGE = "Test crash" 23 | private val DEFAULT_TYPE = Label.Type.BUTTON 24 | private val DEFAULT_ICON: Int? = null 25 | private const val DEFAULT_IS_ENABLED = true 26 | private val DEFAULT_ON_BUTTON_PRESSED: () -> Unit = {} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/Gallery.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class Gallery( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val type: Label.Type = DEFAULT_TYPE, 11 | @DrawableRes val icon: Int? = DEFAULT_ICON, 12 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 13 | val onButtonPressed: () -> Unit = DEFAULT_ON_BUTTON_PRESSED 14 | ) : Component { 15 | 16 | override val id: String = ID 17 | 18 | companion object { 19 | const val ID = "galleryButton" 20 | private const val DEFAULT_TEXT = "Open the gallery" 21 | private val DEFAULT_TYPE = Label.Type.BUTTON 22 | private val DEFAULT_ICON: Int? = null 23 | private const val DEFAULT_IS_ENABLED = true 24 | private val DEFAULT_ON_BUTTON_PRESSED: () -> Unit = {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/Header.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.StringRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | 7 | @Suppress("Unused") 8 | data class Header( 9 | val title: Text, 10 | val subtitle: Text? = DEFAULT_SUBTITLE?.let { Text.CharSequence(it) }, 11 | val text: Text? = DEFAULT_TEXT?.let { Text.CharSequence(it) } 12 | ) : Component
{ 13 | 14 | constructor( 15 | title: CharSequence, 16 | subtitle: CharSequence? = DEFAULT_SUBTITLE, 17 | text: CharSequence? = DEFAULT_TEXT 18 | ) : this( 19 | title = Text.CharSequence(title), 20 | subtitle = subtitle?.let { Text.CharSequence(it) }, 21 | text = text?.let { Text.CharSequence(it) } 22 | ) 23 | 24 | constructor( 25 | @StringRes title: Int, 26 | @StringRes subtitle: Int? = DEFAULT_SUBTITLE_INT, 27 | @StringRes text: Int? = DEFAULT_TEXT_INT 28 | ) : this( 29 | title = Text.ResourceId(title), 30 | subtitle = subtitle?.let { Text.ResourceId(it) }, 31 | text = text?.let { Text.ResourceId(it) } 32 | ) 33 | 34 | override val id: String = ID 35 | 36 | companion object { 37 | const val ID = "header" 38 | private val DEFAULT_SUBTITLE: CharSequence? = null 39 | private val DEFAULT_SUBTITLE_INT: Int? = null 40 | private val DEFAULT_TEXT: CharSequence? = null 41 | private val DEFAULT_TEXT_INT: Int? = null 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/Logs.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import com.kernel.finch.common.contracts.Finch 4 | import com.kernel.finch.common.contracts.component.ExpandableComponent 5 | import com.kernel.finch.common.models.Text 6 | import java.text.SimpleDateFormat 7 | import java.util.* 8 | 9 | data class Logs( 10 | val title: Text = Text.CharSequence(DEFAULT_TITLE), 11 | val maxItemCount: Int = DEFAULT_MAX_ITEM_COUNT, 12 | val timeFormatter: ((Long) -> CharSequence) = { DEFAULT_DATE_FORMAT.format(it) }, 13 | val label: String? = DEFAULT_LABEL, 14 | val isHorizontalScrollEnabled: Boolean = DEFAULT_IS_HORIZONTAL_SCROLL_ENABLED, 15 | override val isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY 16 | ) : ExpandableComponent { 17 | 18 | override val id = formatId(label) 19 | 20 | override fun getHeaderTitle(finch: Finch) = title 21 | 22 | companion object { 23 | private const val DEFAULT_TITLE = "Logs" 24 | private const val DEFAULT_MAX_ITEM_COUNT = 10 25 | private val DEFAULT_DATE_FORMAT by lazy { 26 | SimpleDateFormat( 27 | Finch.LOG_TIME_FORMAT, 28 | Locale.ENGLISH 29 | ) 30 | } 31 | private val DEFAULT_LABEL: String? = null 32 | private const val DEFAULT_IS_HORIZONTAL_SCROLL_ENABLED = false 33 | private const val DEFAULT_IS_EXPANDED_INITIALLY = false 34 | 35 | fun formatId(label: String?) = "logList_$label" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/LoremIpsumGenerator.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class LoremIpsumGenerator( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val minimumWordCount: Int = DEFAULT_MINIMUM_WORD_COUNT, 11 | val maximumWordCount: Int = DEFAULT_MAXIMUM_WORD_COUNT, 12 | val shouldStartWithLoremIpsum: Boolean = DEFAULT_SHOULD_START_WITH_LOREM_IPSUM, 13 | val shouldGenerateSentence: Boolean = DEFAULT_SHOULD_GENERATE_SENTENCE, 14 | val type: Label.Type = DEFAULT_TYPE, 15 | @DrawableRes val icon: Int? = DEFAULT_ICON, 16 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 17 | val onLoremIpsumReady: (generatedText: String) -> Unit 18 | ) : Component { 19 | 20 | override val id: String = ID 21 | 22 | companion object { 23 | const val ID = "loremIpsumGenerator" 24 | private const val DEFAULT_TEXT = "Generate Lorem Ipsum" 25 | private const val DEFAULT_MINIMUM_WORD_COUNT = 5 26 | private const val DEFAULT_MAXIMUM_WORD_COUNT = 20 27 | private const val DEFAULT_SHOULD_START_WITH_LOREM_IPSUM = true 28 | private const val DEFAULT_SHOULD_GENERATE_SENTENCE = false 29 | private val DEFAULT_TYPE = Label.Type.BUTTON 30 | private val DEFAULT_ICON: Int? = null 31 | private const val DEFAULT_IS_ENABLED = true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/NetworkLogs.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import com.kernel.finch.common.contracts.Finch 4 | import com.kernel.finch.common.contracts.component.ExpandableComponent 5 | import com.kernel.finch.common.models.Text 6 | import java.text.SimpleDateFormat 7 | import java.util.* 8 | 9 | data class NetworkLogs( 10 | val title: Text = Text.CharSequence(DEFAULT_TITLE), 11 | val baseUrl: String = DEFAULT_BASE_URL, 12 | val maxItemCount: Int = DEFAULT_MAX_ITEM_COUNT, 13 | val maxItemTitleLength: Int? = DEFAULT_MAX_ITEM_TITLE_LENGTH, 14 | val timeFormatter: ((Long) -> CharSequence) = { DEFAULT_TIME_FORMAT.format(it) }, 15 | val dateTimeFormatter: ((Long) -> CharSequence) = { DEFAULT_DATETIME_FORMAT.format(it) }, 16 | override val isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY 17 | ) : ExpandableComponent { 18 | 19 | override val id: String = ID 20 | 21 | override fun getHeaderTitle(finch: Finch) = title 22 | 23 | companion object { 24 | const val ID = "networkLogs" 25 | private const val DEFAULT_TITLE = "Network logs" 26 | private const val DEFAULT_BASE_URL = "" 27 | private const val DEFAULT_MAX_ITEM_COUNT = 10 28 | private val DEFAULT_MAX_ITEM_TITLE_LENGTH: Int? = null 29 | private val DEFAULT_TIME_FORMAT by lazy { 30 | SimpleDateFormat( 31 | Finch.LOG_TIME_FORMAT, 32 | Locale.ENGLISH 33 | ) 34 | } 35 | private val DEFAULT_DATETIME_FORMAT by lazy { 36 | SimpleDateFormat( 37 | Finch.LOG_DATE_AND_TIME_FORMAT, 38 | Locale.ENGLISH 39 | ) 40 | } 41 | private const val DEFAULT_IS_EXPANDED_INITIALLY = false 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/ScreenCaptureToolbox.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import com.kernel.finch.common.contracts.Finch 4 | import com.kernel.finch.common.contracts.component.ExpandableComponent 5 | import com.kernel.finch.common.models.Text 6 | 7 | data class ScreenCaptureToolbox( 8 | val title: Text = Text.CharSequence(DEFAULT_TITLE), 9 | val imageText: Text? = Text.CharSequence(DEFAULT_IMAGE_TEXT), 10 | val videoText: Text? = Text.CharSequence(DEFAULT_VIDEO_TEXT), 11 | val galleryText: Text = Text.CharSequence(DEFAULT_GALLERY_TEXT), 12 | override val isExpandedInitially: Boolean = DEFAULT_IS_EXPANDED_INITIALLY 13 | ) : ExpandableComponent { 14 | 15 | override val id: String = ID 16 | 17 | override fun getHeaderTitle(finch: Finch) = title 18 | 19 | companion object { 20 | const val ID = "screenCaptureToolbox" 21 | private const val DEFAULT_TITLE = "Screen capture tools" 22 | private const val DEFAULT_IMAGE_TEXT = "Take a screenshot" 23 | private const val DEFAULT_VIDEO_TEXT = "Record a video" 24 | private const val DEFAULT_GALLERY_TEXT = "Open the gallery" 25 | private const val DEFAULT_IS_EXPANDED_INITIALLY = false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/ScreenRecording.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class ScreenRecording( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val type: Label.Type = DEFAULT_TYPE, 11 | @DrawableRes val icon: Int? = DEFAULT_ICON, 12 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 13 | val onButtonPressed: () -> Unit = DEFAULT_ON_BUTTON_PRESSED 14 | ) : Component { 15 | 16 | override val id: String = ID 17 | 18 | companion object { 19 | const val ID = "screenRecording" 20 | private const val DEFAULT_TEXT = "Record a video" 21 | private val DEFAULT_TYPE = Label.Type.BUTTON 22 | private val DEFAULT_ICON: Int? = null 23 | private const val DEFAULT_IS_ENABLED = true 24 | private val DEFAULT_ON_BUTTON_PRESSED: () -> Unit = {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/com/kernel/finch/components/special/Screenshot.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.components.special 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.common.models.Text 6 | import com.kernel.finch.components.Label 7 | 8 | data class Screenshot( 9 | val text: Text = Text.CharSequence(DEFAULT_TEXT), 10 | val type: Label.Type = DEFAULT_TYPE, 11 | @DrawableRes val icon: Int? = DEFAULT_ICON, 12 | val isEnabled: Boolean = DEFAULT_IS_ENABLED, 13 | val onButtonPressed: () -> Unit = DEFAULT_ON_BUTTON_PRESSED 14 | ) : Component { 15 | 16 | override val id: String = ID 17 | 18 | companion object { 19 | const val ID = "screenshot" 20 | private const val DEFAULT_TEXT = "Take a screenshot" 21 | private val DEFAULT_TYPE = Label.Type.BUTTON 22 | private val DEFAULT_ICON: Int? = null 23 | private const val DEFAULT_IS_ENABLED = true 24 | private val DEFAULT_ON_BUTTON_PRESSED: () -> Unit = {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.library" 2 | apply plugin: "kotlin-android" 3 | apply plugin: "kotlin-android-extensions" 4 | apply plugin: "kotlin-kapt" 5 | 6 | android { 7 | compileSdkVersion versions.compileSdk 8 | defaultConfig { 9 | minSdkVersion versions.minSdk 10 | targetSdkVersion versions.targetSdk 11 | versionCode versions.libraryVersionCode 12 | versionName versions.libraryVersion 13 | consumerProguardFiles "consumer-rules.pro" 14 | } 15 | } 16 | 17 | dependencies { 18 | implementation fileTree(dir: 'libs', include: ['*.jar']) 19 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" 20 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines" 21 | implementation "androidx.appcompat:appcompat:$versions.appCompat" 22 | implementation "androidx.recyclerview:recyclerview:$versions.recyclerView" 23 | implementation "androidx.constraintlayout:constraintlayout:$versions.constraintLayout" 24 | implementation "com.google.android.material:material:$versions.material" 25 | implementation "androidx.lifecycle:lifecycle-extensions:$versions.lifecycle" 26 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" 27 | implementation "io.coil-kt:coil:$versions.coil" 28 | implementation "io.coil-kt:coil-video:$versions.coil" 29 | implementation "androidx.room:room-runtime:$versions.room" 30 | kapt "androidx.room:room-compiler:$versions.room" 31 | implementation project(":utils") 32 | implementation project(":common") 33 | } 34 | 35 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { 36 | kotlinOptions { 37 | jvmTarget = "1.8" 38 | freeCompilerArgs = ["-Xjvm-default=enable"] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/core/consumer-rules.pro -------------------------------------------------------------------------------- /core/proguard-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel0x/finch/cceb0ea4fe67dfc3cda2bb927b8ab912b799b459/core/proguard-rules.pro -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 20 | 21 | 24 | 25 | 28 | 29 | 34 | 35 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/FinchCore.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch 2 | 3 | import com.kernel.finch.core.FinchImpl 4 | 5 | object FinchCore { 6 | 7 | lateinit var implementation: FinchImpl 8 | internal set 9 | } -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/data/db/FinchDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.data.db 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import com.kernel.finch.common.loggers.data.models.NetworkLogEntity 8 | import com.kernel.finch.common.loggers.data.models.NetworkLogEntity.Companion.TABLE_NAME 9 | 10 | @Database(entities = [NetworkLogEntity::class], version = 1, exportSchema = false) 11 | internal abstract class FinchDatabase : RoomDatabase() { 12 | 13 | abstract fun networkLog(): NetworkLogDao 14 | 15 | companion object { 16 | 17 | @Volatile 18 | private var instance: FinchDatabase? = null 19 | 20 | fun getInstance(context: Context): FinchDatabase = 21 | instance ?: synchronized(this) { 22 | val newInstance = instance ?: Room.databaseBuilder( 23 | context.applicationContext, 24 | FinchDatabase::class.java, 25 | TABLE_NAME 26 | ).build().also { instance = it } 27 | newInstance 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/data/db/NetworkLogDao.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.data.db 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.* 5 | import com.kernel.finch.common.loggers.data.models.NetworkLogEntity 6 | import com.kernel.finch.common.loggers.data.models.NetworkLogEntity.Companion.TABLE_NAME 7 | 8 | @Dao 9 | internal interface NetworkLogDao { 10 | 11 | @Query("SELECT * FROM $TABLE_NAME ORDER BY requestDate DESC, id DESC") 12 | fun getAll(): LiveData> 13 | 14 | @Query("SELECT * FROM $TABLE_NAME WHERE path LIKE '%' || :search ||'%' ORDER BY requestDate DESC, id DESC") 15 | fun getAll(search: String): LiveData> 16 | 17 | @Query("SELECT * FROM $TABLE_NAME WHERE responseCode LIKE '%' || :responseCode ||'%' ORDER BY requestDate DESC, id DESC") 18 | fun getAll(responseCode: Int?): LiveData> 19 | 20 | @Query("SELECT * FROM $TABLE_NAME WHERE id = :id") 21 | fun getById(id: Long): LiveData 22 | 23 | @Query("DELETE FROM $TABLE_NAME WHERE id = :id") 24 | fun deleteById(id: Long): Int 25 | 26 | @Query("DELETE FROM $TABLE_NAME WHERE requestDate <= :threshold") 27 | fun deleteWhereRequestDate(threshold: Long) 28 | 29 | @Query("DELETE FROM $TABLE_NAME") 30 | fun deleteAll(): Int 31 | 32 | @Insert(onConflict = OnConflictStrategy.REPLACE) 33 | fun insert(entity: NetworkLogEntity): Long 34 | 35 | @Update 36 | fun update(entity: NetworkLogEntity): Int 37 | 38 | @Delete 39 | fun delete(entity: NetworkLogEntity): Int 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/data/models/Period.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.data.models 2 | 3 | internal enum class Period { 4 | ONE_HOUR, 5 | ONE_DAY, 6 | ONE_WEEK, 7 | FOREVER 8 | } -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/CheckBoxCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import android.widget.CheckBox 6 | import com.kernel.finch.common.contracts.component.Cell 7 | import com.kernel.finch.common.contracts.component.ViewHolder 8 | import com.kernel.finch.common.models.Text 9 | import com.kernel.finch.core.R 10 | import com.kernel.finch.core.util.extension.setText 11 | 12 | internal data class CheckBoxCell( 13 | override val id: String, 14 | private val text: Text, 15 | private val isChecked: Boolean, 16 | private val isEnabled: Boolean, 17 | private val onValueChanged: (Boolean) -> Unit 18 | ) : Cell { 19 | 20 | override fun createViewHolderDelegate() = object : ViewHolder.Delegate() { 21 | 22 | override fun createViewHolder(parent: ViewGroup) = CheckBoxViewHolder(parent) 23 | } 24 | 25 | private class CheckBoxViewHolder(parent: ViewGroup) : ViewHolder( 26 | LayoutInflater.from(parent.context).inflate(R.layout.finch_cell_check_box, parent, false) 27 | ) { 28 | 29 | private val checkBox = itemView.findViewById(R.id.finch_check_box) 30 | 31 | override fun bind(model: CheckBoxCell) { 32 | checkBox.run { 33 | setText(model.text) 34 | setOnCheckedChangeListener(null) 35 | isChecked = model.isChecked 36 | isEnabled = model.isEnabled 37 | setOnCheckedChangeListener { _, isChecked -> model.onValueChanged(isChecked) } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/DividerCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import com.kernel.finch.common.contracts.component.Cell 6 | import com.kernel.finch.common.contracts.component.ViewHolder 7 | import com.kernel.finch.core.R 8 | import com.kernel.finch.utils.extensions.colorResource 9 | 10 | internal data class DividerCell( 11 | override val id: String 12 | ) : Cell { 13 | 14 | override fun createViewHolderDelegate() = object : ViewHolder.Delegate() { 15 | 16 | override fun createViewHolder(parent: ViewGroup) = DividerViewHolder(parent) 17 | } 18 | 19 | private class DividerViewHolder(parent: ViewGroup) : ViewHolder( 20 | LayoutInflater.from(parent.context).inflate(R.layout.finch_cell_divider, parent, false) 21 | ) { 22 | 23 | override fun bind(model: DividerCell) = 24 | itemView.setBackgroundColor(itemView.context.colorResource(android.R.attr.textColorPrimary)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/ExpandedItemCheckBoxCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import android.widget.CheckBox 6 | import com.kernel.finch.common.contracts.component.Cell 7 | import com.kernel.finch.common.contracts.component.ViewHolder 8 | import com.kernel.finch.common.models.Text 9 | import com.kernel.finch.core.R 10 | import com.kernel.finch.core.util.extension.setText 11 | 12 | internal data class ExpandedItemCheckBoxCell( 13 | override val id: String, 14 | private val text: Text, 15 | private val isChecked: Boolean, 16 | private val isEnabled: Boolean, 17 | private val onValueChanged: (Boolean) -> Unit 18 | ) : Cell { 19 | 20 | override fun createViewHolderDelegate() = 21 | object : ViewHolder.Delegate() { 22 | 23 | override fun createViewHolder(parent: ViewGroup) = CheckBoxViewHolder(parent) 24 | } 25 | 26 | private class CheckBoxViewHolder(parent: ViewGroup) : 27 | ViewHolder( 28 | LayoutInflater.from(parent.context) 29 | .inflate(R.layout.finch_cell_expanded_item_check_box, parent, false) 30 | ) { 31 | 32 | private val checkBox = itemView.findViewById(R.id.finch_check_box) 33 | 34 | override fun bind(model: ExpandedItemCheckBoxCell) { 35 | checkBox.run { 36 | setText(model.text) 37 | setOnCheckedChangeListener(null) 38 | isChecked = model.isChecked 39 | isEnabled = model.isEnabled 40 | setOnCheckedChangeListener { _, isChecked -> model.onValueChanged(isChecked) } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/ExpandedItemRadioButtonCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import android.widget.RadioButton 6 | import com.kernel.finch.common.contracts.component.Cell 7 | import com.kernel.finch.common.contracts.component.ViewHolder 8 | import com.kernel.finch.common.models.Text 9 | import com.kernel.finch.core.R 10 | import com.kernel.finch.core.util.extension.setText 11 | 12 | internal data class ExpandedItemRadioButtonCell( 13 | override val id: String, 14 | private val text: Text, 15 | private val isChecked: Boolean, 16 | private val isEnabled: Boolean, 17 | private val onValueChanged: (Boolean) -> Unit 18 | ) : Cell { 19 | 20 | override fun createViewHolderDelegate() = 21 | object : ViewHolder.Delegate() { 22 | 23 | override fun createViewHolder(parent: ViewGroup) = SwitchViewHolder(parent) 24 | } 25 | 26 | private class SwitchViewHolder(parent: ViewGroup) : ViewHolder( 27 | LayoutInflater.from(parent.context) 28 | .inflate(R.layout.finch_cell_expanded_item_radio_button, parent, false) 29 | ) { 30 | 31 | private val radioButton = itemView.findViewById(R.id.finch_radio_button) 32 | 33 | override fun bind(model: ExpandedItemRadioButtonCell) = radioButton.run { 34 | setText(model.text) 35 | setOnCheckedChangeListener(null) 36 | isChecked = model.isChecked 37 | isEnabled = model.isEnabled 38 | setOnCheckedChangeListener { _, isChecked -> model.onValueChanged(isChecked) } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/HeaderCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import android.widget.TextView 6 | import com.kernel.finch.common.contracts.component.Cell 7 | import com.kernel.finch.common.contracts.component.ViewHolder 8 | import com.kernel.finch.common.models.Text 9 | import com.kernel.finch.core.R 10 | import com.kernel.finch.core.util.extension.setText 11 | import com.kernel.finch.core.util.extension.visible 12 | 13 | internal data class HeaderCell( 14 | override val id: String, 15 | private val title: Text, 16 | private val subtitle: Text?, 17 | private val text: Text? 18 | ) : Cell { 19 | 20 | override fun createViewHolderDelegate() = object : ViewHolder.Delegate() { 21 | 22 | override fun createViewHolder(parent: ViewGroup) = HeaderViewHolder(parent) 23 | } 24 | 25 | private class HeaderViewHolder(parent: ViewGroup) : ViewHolder( 26 | LayoutInflater.from(parent.context).inflate(R.layout.finch_cell_header, parent, false) 27 | ) { 28 | 29 | private val titleTextView = itemView.findViewById(R.id.finch_title) 30 | private val subtitleTextView = itemView.findViewById(R.id.finch_subtitle) 31 | private val textTextView = itemView.findViewById(R.id.finch_text) 32 | 33 | override fun bind(model: HeaderCell) { 34 | titleTextView.setText(model.title) 35 | subtitleTextView.visible = model.subtitle != null 36 | subtitleTextView.setText(model.subtitle) 37 | textTextView.visible = model.text != null 38 | textTextView.setText(model.text) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/PaddingCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import com.kernel.finch.common.contracts.component.Cell 6 | import com.kernel.finch.common.contracts.component.ViewHolder 7 | import com.kernel.finch.core.R 8 | 9 | internal data class PaddingCell( 10 | override val id: String 11 | ) : Cell { 12 | 13 | override fun createViewHolderDelegate() = object : ViewHolder.Delegate() { 14 | 15 | override fun createViewHolder(parent: ViewGroup) = PaddingViewHolder(parent) 16 | } 17 | 18 | private class PaddingViewHolder(parent: ViewGroup) : 19 | ViewHolder( 20 | LayoutInflater.from(parent.context).inflate(R.layout.finch_cell_padding, parent, false) 21 | ) { 22 | 23 | override fun bind(model: PaddingCell) = Unit 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/ProgressBarCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import com.kernel.finch.common.contracts.component.Cell 6 | import com.kernel.finch.common.contracts.component.ViewHolder 7 | import com.kernel.finch.core.R 8 | 9 | internal data class ProgressBarCell( 10 | override val id: String 11 | ) : Cell { 12 | 13 | override fun createViewHolderDelegate() = object : ViewHolder.Delegate() { 14 | 15 | override fun createViewHolder(parent: ViewGroup) = LoadingIndicatorViewHolder(parent) 16 | } 17 | 18 | private class LoadingIndicatorViewHolder(parent: ViewGroup) : ViewHolder( 19 | LayoutInflater.from(parent.context) 20 | .inflate(R.layout.finch_cell_progress_bar, parent, false) 21 | ) { 22 | 23 | override fun bind(model: ProgressBarCell) = Unit 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/cells/SwitchCell.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.cells 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.appcompat.widget.SwitchCompat 6 | import com.kernel.finch.common.contracts.component.Cell 7 | import com.kernel.finch.common.contracts.component.ViewHolder 8 | import com.kernel.finch.common.models.Text 9 | import com.kernel.finch.core.R 10 | import com.kernel.finch.core.util.extension.setText 11 | 12 | internal data class SwitchCell( 13 | override val id: String, 14 | private val text: Text, 15 | private val isChecked: Boolean, 16 | private val isEnabled: Boolean, 17 | private val onValueChanged: (Boolean) -> Unit 18 | ) : Cell { 19 | 20 | override fun createViewHolderDelegate() = object : ViewHolder.Delegate() { 21 | 22 | override fun createViewHolder(parent: ViewGroup) = SwitchViewHolder(parent) 23 | } 24 | 25 | private class SwitchViewHolder(parent: ViewGroup) : ViewHolder( 26 | LayoutInflater.from(parent.context).inflate(R.layout.finch_cell_switch, parent, false) 27 | ) { 28 | 29 | private val switch = itemView.findViewById(R.id.finch_switch) 30 | 31 | override fun bind(model: SwitchCell) = switch.run { 32 | setText(model.text) 33 | setOnCheckedChangeListener(null) 34 | isChecked = model.isChecked 35 | isEnabled = model.isEnabled 36 | setOnCheckedChangeListener { _, isChecked -> model.onValueChanged(isChecked) } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/AnimationSpeedDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import android.animation.ValueAnimator 4 | import com.kernel.finch.common.contracts.component.Cell 5 | import com.kernel.finch.components.special.AnimationSpeed 6 | import com.kernel.finch.core.list.cells.SwitchCell 7 | import com.kernel.finch.core.list.delegates.shared.ValueWrapperComponentDelegate 8 | 9 | internal class AnimationSpeedDelegate : 10 | ValueWrapperComponentDelegate.Boolean() { 11 | 12 | override fun createCells(component: AnimationSpeed): List> = 13 | getCurrentValue(component).let { currentValue -> 14 | listOf( 15 | SwitchCell( 16 | id = component.id, 17 | text = component.text(currentValue), 18 | isChecked = currentValue, 19 | isEnabled = component.isEnabled, 20 | onValueChanged = { newValue -> setCurrentValue(component, newValue) } 21 | ) 22 | ) 23 | } 24 | 25 | override fun callOnValueChanged( 26 | component: AnimationSpeed, 27 | newValue: kotlin.Boolean 28 | ) { 29 | try { 30 | ValueAnimator::class.java.methods.firstOrNull { it.name == "setDurationScale" } 31 | ?.invoke(null, if (newValue) component.multiplier else 1f) 32 | } catch (_: Throwable) { 33 | } 34 | super.callOnValueChanged(component, newValue) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/AppInfoDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.provider.Settings 6 | import com.kernel.finch.FinchCore 7 | import com.kernel.finch.common.contracts.component.Cell 8 | import com.kernel.finch.common.contracts.component.Component 9 | import com.kernel.finch.components.special.AppInfo 10 | import com.kernel.finch.core.util.createTextComponentFromType 11 | 12 | internal class AppInfoDelegate : Component.Delegate { 13 | 14 | override fun createCells(component: AppInfo): List> = listOf( 15 | createTextComponentFromType( 16 | type = component.type, 17 | id = component.id, 18 | text = component.text, 19 | isEnabled = component.isEnabled, 20 | icon = component.icon, 21 | onItemSelected = { 22 | FinchCore.implementation.currentActivity?.run { 23 | startActivity(Intent().apply { 24 | action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS 25 | data = Uri.fromParts("package", packageName, null) 26 | if (component.shouldOpenInNewTask) { 27 | flags = Intent.FLAG_ACTIVITY_NEW_TASK 28 | } 29 | }) 30 | } 31 | component.onButtonPressed() 32 | } 33 | ) 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/CheckBoxDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.component.Cell 4 | import com.kernel.finch.components.CheckBox 5 | import com.kernel.finch.core.list.cells.CheckBoxCell 6 | import com.kernel.finch.core.list.delegates.shared.ValueWrapperComponentDelegate 7 | 8 | internal class CheckBoxDelegate : ValueWrapperComponentDelegate.Boolean() { 9 | 10 | override fun createCells(component: CheckBox): List> = 11 | getUiValue(component).let { uiValue -> 12 | listOf( 13 | CheckBoxCell( 14 | id = component.id, 15 | text = component.text(uiValue).let { title -> 16 | if (component.shouldRequireConfirmation && hasPendingChanges(component)) 17 | title.with("*") 18 | else 19 | title 20 | }, 21 | isChecked = uiValue, 22 | isEnabled = component.isEnabled, 23 | onValueChanged = { setUiValue(component, it) }) 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/DeveloperOptionsDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import android.content.Intent 4 | import android.provider.Settings 5 | import com.kernel.finch.FinchCore 6 | import com.kernel.finch.common.contracts.component.Cell 7 | import com.kernel.finch.common.contracts.component.Component 8 | import com.kernel.finch.components.special.DeveloperOptions 9 | import com.kernel.finch.core.util.createTextComponentFromType 10 | 11 | internal class DeveloperOptionsDelegate : Component.Delegate { 12 | 13 | override fun createCells(component: DeveloperOptions): List> = listOf( 14 | createTextComponentFromType( 15 | type = component.type, 16 | id = component.id, 17 | text = component.text, 18 | isEnabled = component.isEnabled, 19 | icon = component.icon, 20 | onItemSelected = { 21 | FinchCore.implementation.currentActivity?.run { 22 | startActivity(Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS).apply { 23 | if (component.shouldOpenInNewTask) { 24 | flags = Intent.FLAG_ACTIVITY_NEW_TASK 25 | } 26 | }) 27 | } 28 | component.onButtonPressed() 29 | } 30 | ) 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/DividerDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.component.Cell 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.components.Divider 6 | import com.kernel.finch.core.list.cells.DividerCell 7 | 8 | internal class DividerDelegate : Component.Delegate { 9 | 10 | override fun createCells(component: Divider): List> = 11 | listOf(DividerCell(id = component.id)) 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/ForceCrashDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.component.Cell 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.components.special.ForceCrash 6 | import com.kernel.finch.core.util.createTextComponentFromType 7 | 8 | internal class ForceCrashDelegate : Component.Delegate { 9 | 10 | override fun createCells(component: ForceCrash): List> = listOf( 11 | createTextComponentFromType( 12 | type = component.type, 13 | id = component.id, 14 | text = component.text, 15 | isEnabled = component.isEnabled, 16 | icon = component.icon, 17 | onItemSelected = { 18 | component.onButtonPressed() 19 | throw RuntimeException(component.exception) 20 | } 21 | ) 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/GalleryDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.FinchCore 4 | import com.kernel.finch.common.contracts.component.Cell 5 | import com.kernel.finch.common.contracts.component.Component 6 | import com.kernel.finch.components.special.Gallery 7 | import com.kernel.finch.core.util.createTextComponentFromType 8 | 9 | internal class GalleryDelegate : Component.Delegate { 10 | 11 | override fun createCells(component: Gallery): List> = listOf( 12 | createTextComponentFromType( 13 | type = component.type, 14 | id = component.id, 15 | text = component.text, 16 | isEnabled = component.isEnabled, 17 | icon = component.icon, 18 | onItemSelected = { 19 | component.onButtonPressed() 20 | FinchCore.implementation.openGallery() 21 | } 22 | ) 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/HeaderDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.component.Cell 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.components.special.Header 6 | import com.kernel.finch.core.list.cells.HeaderCell 7 | 8 | internal class HeaderDelegate : Component.Delegate
{ 9 | 10 | override fun createCells(component: Header): List> = listOf( 11 | HeaderCell( 12 | id = component.id, 13 | title = component.title, 14 | subtitle = component.subtitle, 15 | text = component.text 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/ItemListDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.FinchListItem 4 | import com.kernel.finch.common.contracts.component.Cell 5 | import com.kernel.finch.components.ItemList 6 | import com.kernel.finch.core.list.cells.ExpandedItemTextCell 7 | import com.kernel.finch.core.list.delegates.shared.ExpandableComponentDelegate 8 | 9 | internal class ItemListDelegate : ExpandableComponentDelegate> { 10 | 11 | override fun canExpand(component: ItemList) = component.items.isNotEmpty() 12 | 13 | override fun MutableList>.addItems(component: ItemList) { 14 | addAll(component.items.map { item -> 15 | ExpandedItemTextCell( 16 | id = "${component.id}_${item.id}", 17 | text = item.title, 18 | isEnabled = true, 19 | onItemSelected = component.onItemSelected?.let { onItemSelected -> 20 | { 21 | component.items.firstOrNull { it.id == item.id }?.let { onItemSelected(it) } 22 | Unit 23 | } 24 | } 25 | ) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/KeyValueListDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.component.Cell 4 | import com.kernel.finch.components.KeyValueList 5 | import com.kernel.finch.core.list.cells.ExpandedItemKeyValueCell 6 | import com.kernel.finch.core.list.delegates.shared.ExpandableComponentDelegate 7 | 8 | internal class KeyValueListDelegate : ExpandableComponentDelegate { 9 | 10 | override fun canExpand(component: KeyValueList) = component.pairs.isNotEmpty() 11 | 12 | override fun MutableList>.addItems(component: KeyValueList) { 13 | addAll(component.pairs.mapIndexed { index, item -> 14 | ExpandedItemKeyValueCell( 15 | id = "${component.id}_$index", 16 | key = item.first, 17 | value = item.second 18 | ) 19 | }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/kernel/finch/core/list/delegates/LabelDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.kernel.finch.core.list.delegates 2 | 3 | import com.kernel.finch.common.contracts.component.Cell 4 | import com.kernel.finch.common.contracts.component.Component 5 | import com.kernel.finch.components.Label 6 | import com.kernel.finch.core.util.createTextComponentFromType 7 | 8 | internal class LabelDelegate : Component.Delegate