From bdb4daf78fa6dca0230da21042d2629799ca90dc Mon Sep 17 00:00:00 2001 From: storytellerF <34095089+storytellerF@users.noreply.github.com> Date: Sat, 27 Jan 2024 22:00:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E5=85=A5detekt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/GiantExplorer/build.gradle.kts | 36 + app/GiantExplorer/config/detekt/detekt.yml | 1074 +++++++++++++++++ .../plugin_core/FileSystemProviderConstant.kt | 2 +- .../plugin_core/GiantExplorerPlugin.kt | 2 +- .../plugin_core/ExampleUnitTest.kt | 2 +- .../1.json | 200 +++ .../giant_explorer/MigrationTest.kt | 6 +- .../com/storyteller_f/giant_explorer/App.kt | 126 +- .../giant_explorer/control/AboutActivity.kt | 20 +- .../control/DocumentProviderMenuProvider.kt | 4 +- .../giant_explorer/control/FileList.kt | 128 +- .../control/FileListFragment.kt | 147 ++- .../giant_explorer/control/Functions.kt | 28 + .../giant_explorer/control/MainActivity.kt | 146 ++- .../control/SettingsActivity.kt | 2 +- .../control/plugin/FragmentPluginActivity.kt | 26 +- .../control/plugin/PluginInfoFragment.kt | 7 +- .../control/plugin/PluginListFragment.kt | 41 +- .../control/plugin/PluginManageActivity.kt | 3 - .../control/plugin/PluginManager.kt | 62 +- .../control/plugin/WebViewPluginActivity.kt | 97 +- .../control/remote/RemoteDetailFragment.kt | 59 +- .../control/remote/RemoteListFragment.kt | 27 +- .../control/remote/RemoteManagerActivity.kt | 7 +- .../control/root/RootAccessActivity.kt | 14 +- .../control/root/RootAccessStatusFragment.kt | 6 +- .../control/root/RootIntroFragment.kt | 22 +- .../control/task/AddTaskFragment.kt | 20 +- .../task/BackgroundTaskConfigActivity.kt | 6 +- .../task/BackgroundTaskListFragment.kt | 35 +- .../giant_explorer/database/AppDatabase.kt | 17 +- .../database/RemoteAccessSpec.kt | 2 +- .../dialog/FileOperationDialog.kt | 69 +- .../dialog/FilterDialogFragment.kt | 6 +- .../giant_explorer/dialog/NewNameDialog.kt | 2 +- .../giant_explorer/dialog/OpenFileDialog.kt | 21 +- .../giant_explorer/dialog/PropertiesDialog.kt | 25 +- .../dialog/RequestPathDialog.kt | 9 +- .../dialog/SortDialogFragment.kt | 6 +- .../dialog/TaskConfirmDialog.kt | 11 +- .../dialog/VolumeSpaceDialog.kt | 68 +- .../giant_explorer/filter/NameFilter.kt | 5 +- .../giant_explorer/filter/NameSort.kt | 4 +- .../giant_explorer/model/FileModel.kt | 1 - .../service/FileOperateBinder.kt | 67 +- .../service/FileOperateService.kt | 2 +- .../giant_explorer/service/FileService.kt | 2 +- .../service/FileSystemProvider.kt | 10 +- .../service/LocalFileOperationForeman.kt | 27 +- .../utils/BEncodedDictionary.kt | 1 - .../giant_explorer/utils/BEncodedList.kt | 2 +- .../giant_explorer/utils/Decode.kt | 7 +- .../giant_explorer/utils/TorrentFile.kt | 2 +- .../res/layout/fragment_remote_detail.xml | 4 +- .../giant_explorer/utils/TorrentFileTest.kt | 2 +- app/GiantExplorer/settings.gradle.kts | 70 +- plugins/yue/app/build.gradle.kts | 18 +- 57 files changed, 2181 insertions(+), 634 deletions(-) create mode 100644 app/GiantExplorer/config/detekt/detekt.yml create mode 100644 app/GiantExplorer/giant-explorer/schemas/com.storyteller_f.giant_explorer.database.AppDatabase/1.json create mode 100644 app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/Functions.kt diff --git a/app/GiantExplorer/build.gradle.kts b/app/GiantExplorer/build.gradle.kts index 3a269ba..cf17790 100644 --- a/app/GiantExplorer/build.gradle.kts +++ b/app/GiantExplorer/build.gradle.kts @@ -1,3 +1,6 @@ +import io.gitlab.arturbosch.detekt.Detekt +import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask + buildscript { dependencies { val versionManager: String by project @@ -18,4 +21,37 @@ plugins { id("org.jetbrains.kotlin.android") version kotlinVersion apply false id("org.jetbrains.kotlin.jvm") version kotlinVersion apply false id("com.google.devtools.ksp") version kspVersion apply false + id("io.gitlab.arturbosch.detekt") version "1.23.1" +} + +subprojects { + apply(plugin = "io.gitlab.arturbosch.detekt") + detekt { + source.setFrom( + io.gitlab.arturbosch.detekt.extensions.DetektExtension.DEFAULT_SRC_DIR_JAVA, + io.gitlab.arturbosch.detekt.extensions.DetektExtension.DEFAULT_TEST_SRC_DIR_JAVA, + io.gitlab.arturbosch.detekt.extensions.DetektExtension.DEFAULT_SRC_DIR_KOTLIN, + io.gitlab.arturbosch.detekt.extensions.DetektExtension.DEFAULT_TEST_SRC_DIR_KOTLIN, + ) + buildUponDefaultConfig = true + autoCorrect = true + config.setFrom("$rootDir/config/detekt/detekt.yml") + baseline = file("$rootDir/config/detekt/baseline.xml") + } + dependencies { + val detektVersion = "1.23.1" + + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-rules-libraries:$detektVersion") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-rules-ruleauthors:$detektVersion") + } + + tasks.withType().configureEach { + jvmTarget = "1.8" + basePath = rootDir.absolutePath + } + tasks.withType().configureEach { + jvmTarget = "1.8" + } + } \ No newline at end of file diff --git a/app/GiantExplorer/config/detekt/detekt.yml b/app/GiantExplorer/config/detekt/detekt.yml new file mode 100644 index 0000000..a296728 --- /dev/null +++ b/app/GiantExplorer/config/detekt/detekt.yml @@ -0,0 +1,1074 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + checkExhaustiveness: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + # - 'SarifOutputReport' + +comments: + active: true + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false + UndocumentedPublicClass: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + searchInProtectedClass: false + UndocumentedPublicFunction: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedFunction: false + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedProperty: false + +complexity: + active: true + CognitiveComplexMethod: + active: false + threshold: 15 + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + ignoreOverloaded: false + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: false + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: + active: true + threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: true + ignoreOverridden: true + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunSwallowedCancellation: + active: false + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: + active: false + ObjectExtendsThrowable: + active: false + PrintStackTrace: + active: true + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: false + SwallowedException: + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +naming: + active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + ignoreAnnotated: ['Composable'] + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + InvalidPackageDeclaration: + active: true + rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9_]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 + VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: false + threshold: 3 + ForEachOnRange: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + SpreadOperator: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryPartOfBinaryExpression: + active: false + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastNullableToNonNullableType: + active: false + CastToNullableType: + active: false + Deprecation: + active: false + DontDowncastCollectionTypes: + active: false + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + ElseCaseInsteadOfExhaustiveWhen: + active: false + ignoredSubjectTypes: [] + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: false + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + restrictToConfig: true + returnValueAnnotations: + - 'CheckResult' + - '*.CheckResult' + - 'CheckReturnValue' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - 'CanIgnoreReturnValue' + - '*.CanIgnoreReturnValue' + returnValueTypes: + - 'kotlin.sequences.Sequence' + - 'kotlinx.coroutines.flow.*Flow' + - 'java.util.stream.*Stream' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: false + excludes: ['**/*.kts'] + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + PropertyUsedBeforeDeclaration: + active: false + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullCheck: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: true + WrongEqualsTypeParameter: + active: true + +style: + active: true + AlsoCouldBeApply: + active: false + BracesOnIfStatements: + active: false + singleLine: 'never' + multiLine: 'always' + BracesOnWhenStatements: + active: false + singleLine: 'necessary' + multiLine: 'consistent' + CanBeNonNullable: + active: false + CascadingCallWrapping: + active: false + includeElvis: true + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: + - 'to' + allowOperators: false + DataClassShouldBeImmutable: + active: false + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 10 + DoubleNegativeLambda: + active: false + negativeFunctions: + - reason: 'Use `takeIf` instead.' + value: 'takeUnless' + - reason: 'Use `all` instead.' + value: 'none' + negativeFunctionNameParts: + - 'not' + - 'non' + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: true + ExpressionBodySyntax: + active: false + includeLineWrapping: false + ForbiddenAnnotation: + active: false + annotations: + - reason: 'it is a java annotation. Use `Suppress` instead.' + value: 'java.lang.SuppressWarnings' + - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.' + value: 'java.lang.Deprecated' + - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.' + value: 'java.lang.annotation.Documented' + - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.' + value: 'java.lang.annotation.Target' + - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.' + value: 'java.lang.annotation.Retention' + - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.' + value: 'java.lang.annotation.Repeatable' + - reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265' + value: 'java.lang.annotation.Inherited' + ForbiddenComment: + active: true + comments: + - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' + value: 'FIXME:' + - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' + value: 'STOPSHIP:' + - reason: 'Forbidden TODO todo marker in comment, please do the changes.' + value: 'TODO:' + allowedPatterns: '' + ForbiddenImport: + active: false + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: + - reason: 'print does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.print' + - reason: 'println does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.println' + ForbiddenSuppress: + active: false + rules: [] + ForbiddenVoid: + active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: [] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesLoops: + active: false + MaxChainedCallsOnSameLine: + active: false + maxChainedCalls: 5 + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + excludeRawStrings: true + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: false + MultilineRawStringIndentation: + active: false + indentSize: 4 + trimmingMethods: + - 'trimIndent' + - 'trimMargin' + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + NullableBooleanCheck: + active: false + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: true + max: 2 + excludedFunctions: + - 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: true + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: false + StringShouldBeRawString: + active: false + maxEscapedCharacterCount: 2 + ignoredCharacters: [] + ThrowsCount: + active: true + max: 2 + excludeGuardClauses: true + TrailingWhitespace: + active: false + TrimMultilineRawString: + active: false + trimmingMethods: + - 'trimIndent' + - 'trimMargin' + UnderscoresInNumericLiterals: + active: false + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: false + UnnecessaryBracesAroundTrailingLambda: + active: false + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: false + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + allowForUnclearPrecedence: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedParameter: + active: true + allowedNames: 'ignored|expected' + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: true + allowedNames: '' + UnusedPrivateProperty: + active: true + allowedNames: '_|ignored|expected|serialVersionUID' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true + UseCheckOrError: + active: true + UseDataClass: + active: false + allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + ignoreWhenContainingVariableDeclaration: false + UseIsNullOrEmpty: + active: true + UseLet: + active: false + UseOrEmpty: + active: true + UseRequire: + active: true + UseRequireNotNull: + active: true + UseSumOfInsteadOfFlatMapSize: + active: false + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + VarCouldBeVal: + active: true + ignoreLateinitVar: false + WildcardImport: + active: false + excludeImports: + - 'java.util.*' + +formatting: + active: true + android: false + autoCorrect: true + AnnotationOnSeparateLine: + active: true + autoCorrect: true + indentSize: 4 + AnnotationSpacing: + active: true + autoCorrect: true + ArgumentListWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + BlockCommentInitialStarAlignment: + active: true + autoCorrect: true + ChainWrapping: + active: true + autoCorrect: true + indentSize: 4 + ClassName: + active: false + CommentSpacing: + active: true + autoCorrect: true + CommentWrapping: + active: true + autoCorrect: true + indentSize: 4 + ContextReceiverMapping: + active: false + autoCorrect: true + maxLineLength: 120 + indentSize: 4 + DiscouragedCommentLocation: + active: false + autoCorrect: true + EnumEntryNameCase: + active: true + autoCorrect: true + EnumWrapping: + active: false + autoCorrect: true + indentSize: 4 + Filename: + active: true + FinalNewline: + active: true + autoCorrect: true + insertFinalNewLine: true + FunKeywordSpacing: + active: true + autoCorrect: true + FunctionName: + active: false + FunctionReturnTypeSpacing: + active: true + autoCorrect: true + maxLineLength: 120 + FunctionSignature: + active: false + autoCorrect: true + forceMultilineWhenParameterCountGreaterOrEqualThan: 2147483647 + functionBodyExpressionWrapping: 'default' + maxLineLength: 120 + indentSize: 4 + FunctionStartOfBodySpacing: + active: true + autoCorrect: true + FunctionTypeReferenceSpacing: + active: true + autoCorrect: true + IfElseBracing: + active: false + autoCorrect: true + indentSize: 4 + IfElseWrapping: + active: false + autoCorrect: true + indentSize: 4 + ImportOrdering: + active: true + autoCorrect: true + layout: '*,java.**,javax.**,kotlin.**,^' + Indentation: + active: true + autoCorrect: true + indentSize: 4 + KdocWrapping: + active: true + autoCorrect: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ignoreBackTickedIdentifier: false + ModifierListSpacing: + active: true + autoCorrect: true + ModifierOrdering: + active: true + autoCorrect: true + MultiLineIfElse: + active: true + autoCorrect: true + indentSize: 4 + MultilineExpressionWrapping: + active: false + autoCorrect: true + indentSize: 4 + NoBlankLineBeforeRbrace: + active: true + autoCorrect: true + NoBlankLineInList: + active: false + autoCorrect: true + NoBlankLinesInChainedMethodCalls: + active: true + autoCorrect: true + NoConsecutiveBlankLines: + active: true + autoCorrect: true + NoConsecutiveComments: + active: false + NoEmptyClassBody: + active: true + autoCorrect: true + NoEmptyFirstLineInClassBody: + active: false + autoCorrect: true + indentSize: 4 + NoEmptyFirstLineInMethodBlock: + active: true + autoCorrect: true + NoLineBreakAfterElse: + active: true + autoCorrect: true + NoLineBreakBeforeAssignment: + active: true + autoCorrect: true + NoMultipleSpaces: + active: true + autoCorrect: true + NoSemicolons: + active: true + autoCorrect: true + NoSingleLineBlockComment: + active: false + autoCorrect: true + indentSize: 4 + NoTrailingSpaces: + active: true + autoCorrect: true + NoUnitReturn: + active: true + autoCorrect: true + NoUnusedImports: + active: true + autoCorrect: true + NoWildcardImports: + active: false + packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' + NullableTypeSpacing: + active: true + autoCorrect: true + PackageName: + active: false + autoCorrect: true + ParameterListSpacing: + active: false + autoCorrect: true + ParameterListWrapping: + active: true + autoCorrect: true + maxLineLength: 120 + indentSize: 4 + ParameterWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + PropertyName: + active: false + PropertyWrapping: + active: true + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + SpacingAroundAngleBrackets: + active: true + autoCorrect: true + SpacingAroundColon: + active: true + autoCorrect: true + SpacingAroundComma: + active: true + autoCorrect: true + SpacingAroundCurly: + active: true + autoCorrect: true + SpacingAroundDot: + active: true + autoCorrect: true + SpacingAroundDoubleColon: + active: true + autoCorrect: true + SpacingAroundKeyword: + active: true + autoCorrect: true + SpacingAroundOperators: + active: true + autoCorrect: true + SpacingAroundParens: + active: true + autoCorrect: true + SpacingAroundRangeOperator: + active: true + autoCorrect: true + SpacingAroundUnaryOperator: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: true + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: true + autoCorrect: true + SpacingBetweenFunctionNameAndOpeningParenthesis: + active: true + autoCorrect: true + StringTemplate: + active: true + autoCorrect: true + StringTemplateIndent: + active: false + autoCorrect: true + indentSize: 4 + TrailingCommaOnCallSite: + active: false + autoCorrect: true + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: false + autoCorrect: true + useTrailingCommaOnDeclarationSite: true + TryCatchFinallySpacing: + active: false + autoCorrect: true + indentSize: 4 + TypeArgumentListSpacing: + active: false + autoCorrect: true + indentSize: 4 + TypeParameterListSpacing: + active: false + autoCorrect: true + indentSize: 4 + UnnecessaryParenthesesBeforeTrailingLambda: + active: true + autoCorrect: true + Wrapping: + active: false + autoCorrect: true + indentSize: 4 + maxLineLength: 120 + +libraries: + active: true + ForbiddenPublicDataClass: + active: false + ignorePackages: + - '*.internal' + - '*.internal.*' + LibraryCodeMustSpecifyReturnType: + active: false + allowOmitUnit: false + LibraryEntitiesShouldNotBePublic: + active: false + +ruleauthors: + active: true + UseEntityAtName: + active: true + ViolatesTypeResolutionRequirements: + active: true diff --git a/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/FileSystemProviderConstant.kt b/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/FileSystemProviderConstant.kt index bcf7ed7..eb91bc4 100644 --- a/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/FileSystemProviderConstant.kt +++ b/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/FileSystemProviderConstant.kt @@ -2,4 +2,4 @@ package com.storyteller_f.plugin_core object FileSystemProviderConstant { const val FILE_PATH = "file path" -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/GiantExplorerPlugin.kt b/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/GiantExplorerPlugin.kt index d282123..a465812 100644 --- a/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/GiantExplorerPlugin.kt +++ b/app/GiantExplorer/giant-explorer-plugin-core/src/main/java/com/storyteller_f/plugin_core/GiantExplorerPlugin.kt @@ -59,4 +59,4 @@ interface GiantExplorerPluginManager { fun resolveParentPath(uriString: String): String? suspend fun isFile(uriString: String): Boolean -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer-plugin-core/src/test/java/com/storyteller_f/plugin_core/ExampleUnitTest.kt b/app/GiantExplorer/giant-explorer-plugin-core/src/test/java/com/storyteller_f/plugin_core/ExampleUnitTest.kt index 7783906..b89716a 100644 --- a/app/GiantExplorer/giant-explorer-plugin-core/src/test/java/com/storyteller_f/plugin_core/ExampleUnitTest.kt +++ b/app/GiantExplorer/giant-explorer-plugin-core/src/test/java/com/storyteller_f/plugin_core/ExampleUnitTest.kt @@ -13,4 +13,4 @@ class ExampleUnitTest { fun addition_isCorrect() { assertEquals(4, 2 + 2) } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/schemas/com.storyteller_f.giant_explorer.database.AppDatabase/1.json b/app/GiantExplorer/giant-explorer/schemas/com.storyteller_f.giant_explorer.database.AppDatabase/1.json new file mode 100644 index 0000000..0994c2e --- /dev/null +++ b/app/GiantExplorer/giant-explorer/schemas/com.storyteller_f.giant_explorer.database.AppDatabase/1.json @@ -0,0 +1,200 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "1098f6b8c9e39b1a2ea5d54392f98b85", + "entities": [ + { + "tableName": "file-size-record", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastUpdateTime` INTEGER NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdateTime", + "columnName": "lastUpdateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "file-md-record", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `data` TEXT NOT NULL, `lastUpdateTime` INTEGER NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdateTime", + "columnName": "lastUpdateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "file-torrent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `torrent` TEXT NOT NULL, `lastUpdateTime` INTEGER NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "torrent", + "columnName": "torrent", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdateTime", + "columnName": "lastUpdateTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "big-time-task", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `enable` INTEGER NOT NULL, `category` TEXT NOT NULL, PRIMARY KEY(`uri`, `category`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "enable", + "columnName": "enable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "category" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "remote-access", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`server` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `share` TEXT NOT NULL DEFAULT '', `type` TEXT NOT NULL DEFAULT 'ftp', PRIMARY KEY(`server`, `port`, `user`, `password`, `share`, `type`))", + "fields": [ + { + "fieldPath": "server", + "columnName": "server", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "port", + "columnName": "port", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "user", + "columnName": "user", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "share", + "columnName": "share", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'ftp'" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "server", + "port", + "user", + "password", + "share", + "type" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1098f6b8c9e39b1a2ea5d54392f98b85')" + ] + } +} \ No newline at end of file diff --git a/app/GiantExplorer/giant-explorer/src/androidTest/java/com/storyteller_f/giant_explorer/MigrationTest.kt b/app/GiantExplorer/giant-explorer/src/androidTest/java/com/storyteller_f/giant_explorer/MigrationTest.kt index 00fdabb..4dbd101 100644 --- a/app/GiantExplorer/giant-explorer/src/androidTest/java/com/storyteller_f/giant_explorer/MigrationTest.kt +++ b/app/GiantExplorer/giant-explorer/src/androidTest/java/com/storyteller_f/giant_explorer/MigrationTest.kt @@ -6,7 +6,7 @@ import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.SupportSQLiteDatabase import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.storyteller_f.giant_explorer.database.LocalDatabase +import com.storyteller_f.giant_explorer.database.AppDatabase import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -25,7 +25,7 @@ class MigrationTest { @get:Rule val helper: MigrationTestHelper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), - LocalDatabase::class.java, + AppDatabase::class.java, mutableListOf(elements) ) @@ -41,7 +41,7 @@ class MigrationTest { // once all migrations execute. Room.databaseBuilder( InstrumentationRegistry.getInstrumentation().targetContext, - LocalDatabase::class.java, + AppDatabase::class.java, TEST_DB ).build().apply { openHelper.writableDatabase.close() diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/App.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/App.kt index b3b2ed9..a36320b 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/App.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/App.kt @@ -58,6 +58,10 @@ val pluginManagerRegister = PluginManager() val defaultFactory = object : ViewModelProvider.Factory { } +const val DEFAULT_DEBOUNCE = 200L +const val DEFAULT_WEBVIEW_HEIGHT = 0.7f +const val DEFAULT_BUFFER_SIZE = 1024 + object WorkCategory { const val MESSAGE_DIGEST = "message-digest" const val FOLDER_SIZE = "folder-size" @@ -82,12 +86,15 @@ class App : Application() { WorkCategory.FOLDER_SIZE -> OneTimeWorkRequestBuilder() else -> OneTimeWorkRequestBuilder() }.setInputData( - Data.Builder().putStringArray("folders", entry.value.mapNotNull { - when { - !it.enable -> null - else -> it.uri.toString() - } - }.toTypedArray()) + Data.Builder().putStringArray( + "folders", + entry.value.mapNotNull { + when { + !it.enable -> null + else -> it.uri.toString() + } + }.toTypedArray() + ) .build() ).build() ) @@ -116,12 +123,11 @@ class App : Application() { Security.removeProvider("BC") Security.insertProviderAt(BouncyCastleProvider(), 1) } - } fun refreshPlugin(context: Context) { - File(context.filesDir, "plugins").listFiles { it -> - it.extension == "apk" || it.extension == "zip" + File(context.filesDir, "plugins").listFiles { file -> + file.extension == "apk" || file.extension == "zip" }?.forEach { pluginManagerRegister.foundPlugin(it) } @@ -151,18 +157,22 @@ abstract class BigTimeWorker( else -> doWork(context, uriString) } } - if (results.none { it is WorkerResult.Failure || it is WorkerResult.Stopped }) Result.success() - else Result.failure( - Data.Builder().putString( - "error", - results.joinToString(",") { - when (it) { - is WorkerResult.Stopped -> "stop" - is WorkerResult.Failure -> it.exception.exceptionMessage - else -> "" + if (results.none { it is WorkerResult.Failure || it is WorkerResult.Stopped }) { + Result.success() + } else { + Result.failure( + Data.Builder().putString( + "error", + results.joinToString(",") { + when (it) { + is WorkerResult.Stopped -> "stop" + is WorkerResult.Failure -> it.exception.exceptionMessage + else -> "" + } } - }).build() - ) + ).build() + ) + } } } @@ -177,48 +187,45 @@ class FolderWorker(context: Context, workerParams: WorkerParameters) : val fileInstance = getFileInstance(context, uri) val record = context.requireDatabase.sizeDao().search(uri) val lastModified = fileInstance.getFileInfo().time.lastModified ?: 0 - if (record != null && record.lastUpdateTime > lastModified) return WorkerResult.SizeWorker( - record.size - ) - val listSafe = fileInstance.list() - val mapNullNull = listSafe.directories.map { - if (isStopped) return WorkerResult.Stopped - doWork(context, it.fullPath) - } - val filter = - mapNullNull.filter { it is WorkerResult.Failure || it is WorkerResult.Stopped } - if (filter.valid()) { - return filter.first() - } - - val filesSize = - listSafe.files.map { - if (isStopped) return WorkerResult.Stopped - (it.kind as FileKind.File).size - }.plus(0).reduce { acc, s -> - if (isStopped) return WorkerResult.Stopped - acc + s + if (record != null && record.lastUpdateTime > lastModified) { + WorkerResult.SizeWorker( + record.size + ) + } else { + val listSafe = fileInstance.list() + val mapNullNull = listSafe.directories.map { + doWork(context, it.fullPath) + } + val filter = + mapNullNull.filter { it is WorkerResult.Failure || it is WorkerResult.Stopped } + if (filter.valid()) { + return filter.first() } - val size = - filesSize + mapNullNull.map { (it as WorkerResult.SizeWorker).size }.plus(0) - .reduce { acc, s -> - if (isStopped) return WorkerResult.Stopped + + val filesSize = + listSafe.files.map { + (it.kind as FileKind.File).size + }.plus(0).reduce { acc, s -> acc + s } - context.requireDatabase.sizeDao() - .save(FileSizeRecord(uri, size, System.currentTimeMillis())) - WorkerResult.SizeWorker(size) + val size = + filesSize + mapNullNull.map { (it as WorkerResult.SizeWorker).size }.plus(0) + .reduce { acc, s -> + acc + s + } + context.requireDatabase.sizeDao() + .save(FileSizeRecord(uri, size, System.currentTimeMillis())) + WorkerResult.SizeWorker(size) + } } catch (e: Exception) { Log.e(TAG, "work: ", e) WorkerResult.Failure(e) } - } companion object { private const val TAG = "App" } - } class MDWorker(context: Context, workerParams: WorkerParameters) : @@ -230,11 +237,9 @@ class MDWorker(context: Context, workerParams: WorkerParameters) : val fileInstance = getFileInstance(context, uri) val listSafe = fileInstance.list() listSafe.directories.mapNullNull { - if (isStopped) return WorkerResult.Stopped doWork(context, it.fullPath) } listSafe.files.forEach { - if (isStopped) return WorkerResult.Stopped val child = uri.buildUpon().path(it.fullPath).build() val search = context.requireDatabase.mdDao().search(child) val lastModified = it.time.lastModified ?: 0 @@ -246,7 +251,6 @@ class MDWorker(context: Context, workerParams: WorkerParameters) : } catch (e: Exception) { WorkerResult.Failure(e) } - } private suspend fun processAndSave( @@ -264,7 +268,6 @@ class MDWorker(context: Context, workerParams: WorkerParameters) : } companion object - } class TorrentWorker(context: Context, workerParams: WorkerParameters) : @@ -276,13 +279,11 @@ class TorrentWorker(context: Context, workerParams: WorkerParameters) : val fileInstance = getFileInstance(context, uri) val listSafe = fileInstance.list() listSafe.directories.mapNullNull { - if (isStopped) return WorkerResult.Stopped doWork(context, it.fullPath) } listSafe.files.filter { it.extension == "torrent" }.forEach { - if (isStopped) return WorkerResult.Stopped val child = uri.buildUpon().path(it.fullPath).build() val search = context.requireDatabase.torrentDao().search(child) val lastModified = it.time.lastModified ?: 0 @@ -295,7 +296,6 @@ class TorrentWorker(context: Context, workerParams: WorkerParameters) : Log.e(TAG, "work: ", e) WorkerResult.Failure(e) } - } private suspend fun processAndSave( @@ -321,7 +321,6 @@ class TorrentWorker(context: Context, workerParams: WorkerParameters) : companion object { private const val TAG = "App" } - } sealed class WorkerResult { @@ -350,10 +349,11 @@ inline fun List.mapNullNull( return if (hasNull) null else destination } -const val pc_end_on = 1024 +const val PC_END_ON = 1024 +const val RADIX = 16 suspend fun getFileMD5(fileInstance: FileInstance): String? { - val buffer = ByteArray(pc_end_on) + val buffer = ByteArray(PC_END_ON) return try { var len: Int val digest = MessageDigest.getInstance("MD5") @@ -364,10 +364,10 @@ suspend fun getFileMD5(fileInstance: FileInstance): String? { } } val bigInt = BigInteger(1, digest.digest()) - bigInt.toString(16) - } catch (e: Exception) { + bigInt.toString(RADIX) + } catch (_: Exception) { null } } -fun Collection?.valid(): Boolean = !isNullOrEmpty() \ No newline at end of file +fun Collection?.valid(): Boolean = !isNullOrEmpty() diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/AboutActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/AboutActivity.kt index 1bc3fdb..4363326 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/AboutActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/AboutActivity.kt @@ -11,9 +11,10 @@ import androidx.browser.customtabs.CustomTabsSession import com.bumptech.glide.Glide import com.storyteller_f.common_ui.CommonActivity import com.storyteller_f.common_ui.setOnClick -import com.storyteller_f.giant_explorer.control.root.MagiskUrl +import com.storyteller_f.giant_explorer.DEFAULT_WEBVIEW_HEIGHT +import com.storyteller_f.giant_explorer.control.root.KERNEL_SU_URL +import com.storyteller_f.giant_explorer.control.root.MAGISK_URL import com.storyteller_f.giant_explorer.control.root.ScreenMetricsCompat -import com.storyteller_f.giant_explorer.control.root.kernelSuUrl import com.storyteller_f.giant_explorer.databinding.ActivityAboutBinding import com.storyteller_f.ui_list.event.viewBinding @@ -24,13 +25,14 @@ class AboutActivity : CommonActivity() { override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) { client.warmup(0) newSession = client.newSession(object : CustomTabsCallback() { - }) - newSession?.mayLaunchUrl(Uri.parse(MagiskUrl), null, null) - newSession?.mayLaunchUrl(Uri.parse(kernelSuUrl), null, null) + newSession?.mayLaunchUrl(Uri.parse(MAGISK_URL), null, null) + newSession?.mayLaunchUrl(Uri.parse(KERNEL_SU_URL), null, null) } - override fun onServiceDisconnected(name: ComponentName) {} + override fun onServiceDisconnected(name: ComponentName) { + newSession = null + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -40,7 +42,9 @@ class AboutActivity : CommonActivity() { binding.image.setOnClick { val newSession = newSession val height = ScreenMetricsCompat.getScreenSize(this).height - val builder = CustomTabsIntent.Builder().setInitialActivityHeightPx((height * 0.7).toInt()) + val builder = CustomTabsIntent.Builder().setInitialActivityHeightPx( + (height * DEFAULT_WEBVIEW_HEIGHT).toInt() + ) if (newSession != null) builder.setSession(newSession) val customTabsIntent = builder.build() customTabsIntent.launchUrl(this, Uri.parse("https://github.com/storytellerF/common-ui-list-structure")) @@ -50,4 +54,4 @@ class AboutActivity : CommonActivity() { companion object { private const val CUSTOM_TAB_PACKAGE_NAME = "com.android.chrome" // Change when in stable } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/DocumentProviderMenuProvider.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/DocumentProviderMenuProvider.kt index 3fbb7fc..46dac1e 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/DocumentProviderMenuProvider.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/DocumentProviderMenuProvider.kt @@ -1,13 +1,11 @@ package com.storyteller_f.giant_explorer.control import android.content.Intent -import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.net.Uri import android.view.Menu import android.view.SubMenu import android.widget.Toast -import androidx.core.graphics.drawable.toBitmap import androidx.core.net.toUri import androidx.lifecycle.LifecycleOwner import com.storyteller_f.common_ui.scope @@ -100,4 +98,4 @@ class DocumentProviderMenuProvider( } } } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileList.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileList.kt index 1f82713..31b7efb 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileList.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileList.kt @@ -47,15 +47,16 @@ import com.storyteller_f.file_system_ktx.fileIcon import com.storyteller_f.file_system_ktx.isDirectory import com.storyteller_f.file_system_ktx.isFile import com.storyteller_f.filter_core.Filter +import com.storyteller_f.giant_explorer.DEFAULT_DEBOUNCE +import com.storyteller_f.giant_explorer.PC_END_ON import com.storyteller_f.giant_explorer.R -import com.storyteller_f.giant_explorer.database.LocalDatabase +import com.storyteller_f.giant_explorer.database.AppDatabase import com.storyteller_f.giant_explorer.database.requireDatabase import com.storyteller_f.giant_explorer.databinding.ViewHolderFileBinding import com.storyteller_f.giant_explorer.databinding.ViewHolderFileGridBinding import com.storyteller_f.giant_explorer.dialog.activeFilters import com.storyteller_f.giant_explorer.dialog.activeSortChains import com.storyteller_f.giant_explorer.model.FileModel -import com.storyteller_f.giant_explorer.pc_end_on import com.storyteller_f.sort_core.config.SortChain import com.storyteller_f.sort_core.config.SortChains import com.storyteller_f.ui_list.adapter.SimpleSourceAdapter @@ -70,6 +71,7 @@ import com.storyteller_f.ui_list.ui.ListWithState import com.storyteller_f.ui_list.ui.toggle import com.storyteller_f.ui_list.ui.valueContains import kotlinx.coroutines.launch +import kotlinx.coroutines.yield import java.util.Locale class FileListViewModel(stateHandle: SavedStateHandle) : ViewModel() { @@ -100,8 +102,9 @@ class FileListObserver( when (owner) { is Fragment -> owner.requireActivity().application is ComponentActivity -> owner.application - else -> throw Exception("unrecognized ${owner.javaClass}") - }, it.uri + else -> throw IllegalArgumentException("unrecognized ${owner.javaClass}") + }, + it.uri ) } @@ -110,14 +113,15 @@ class FileListObserver( when (owner) { is Fragment -> owner.requireDatabase is Context -> owner.requireDatabase - else -> throw Exception("unrecognized ${owner.javaClass}") + else -> throw IllegalArgumentException("unrecognized ${owner.javaClass}") } to session.selected }, { (database, selected) -> SearchProducer(fileSearchService(database)) { fileModel, _, sq -> FileItemHolder(fileModel, selected.value.orEmpty(), sq.display) } - }) + } + ) fun setup( listWithState: ListWithState, @@ -128,7 +132,6 @@ class FileListObserver( with(owner) { setup(listWithState, adapter, rightSwipe, updatePath) } - } private fun T.setup( @@ -137,7 +140,6 @@ class FileListObserver( rightSwipe: (FileItemHolder) -> Unit, updatePath: (String) -> Unit ) { - fileListViewModel.displayGrid.observe(owner) { listWithState.recyclerView.isVisible = false adapter.submitData(cycle, PagingData.empty()) @@ -177,9 +179,11 @@ class FileListObserver( owner, plugLayoutManager = false, dampingSwipe = { viewHolder, direction -> - if (direction == ItemTouchHelper.LEFT) + if (direction == ItemTouchHelper.LEFT) { session.selected.toggle(viewHolder) - else rightSwipe(viewHolder.itemHolder as FileItemHolder) + } else { + rightSwipe(viewHolder.itemHolder as FileItemHolder) + } }, flash = ListWithState.Companion::remote ) @@ -188,7 +192,7 @@ class FileListObserver( } session.fileInstance.observe(owner) { val path = it.uri - //检查权限 + // 检查权限 owner.lifecycleScope.launch { if (!checkFilePermission(path)) { if (requestFilePermission(path)) { @@ -203,7 +207,7 @@ class FileListObserver( activeFilters.same, activeSortChains.same, fileListViewModel.displayGrid - ).wait5().distinctUntilChanged().debounce(200) + ).wait5().distinctUntilChanged().debounce(DEFAULT_DEBOUNCE) .observe(owner) { (fileInstance, filterHiddenFile, filters, sortChains, d5) -> val display = if (d5) "grid" else "" @@ -220,12 +224,10 @@ class FileListObserver( adapter.submitData(pagingData) } } - } } } - private val LiveData>.same get() = distinctUntilChangedBy { sort1, sort2 -> sort1.same(sort2) @@ -242,7 +244,6 @@ class FileItemHolder( override fun areContentsTheSame(other: DataItemHolder): Boolean { return (other as FileItemHolder).file == file } - } @BindItemHolder(FileItemHolder::class, type = "grid") @@ -253,7 +254,6 @@ class FileGridViewHolder(private val binding: ViewHolderFileGridBinding) : binding.fileIcon.fileIcon(itemHolder.file.item) binding.symLink.isVisible = itemHolder.file.isSymLink } - } @BindItemHolder(FileItemHolder::class) @@ -293,10 +293,7 @@ class FileViewHolder(private val binding: ViewHolderFileBinding) : file.item.dragSupport(binding.root) } binding.symLink.isVisible = file.isSymLink - } - - } @RequiresApi(Build.VERSION_CODES.N) @@ -308,42 +305,43 @@ private fun FileInfo.dragSupport(root: ConstraintLayout) { }.apply { attach() } - root.setOnDragListener(if (!isDirectory) null - else - { v, event -> - when (event.action) { - DragEvent.ACTION_DRAG_STARTED -> { - val clipDescription = event.clipDescription - clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN) - && clipDescription.label == FileListFragment.CLIP_DATA_KEY - } + root.setOnDragListener( + if (!isDirectory) { + null + } else { + { v, event -> + when (event.action) { + DragEvent.ACTION_DRAG_STARTED -> { + val clipDescription = event.clipDescription + clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN) && + clipDescription.label == FileListFragment.CLIP_DATA_KEY + } - DragEvent.ACTION_DROP -> { - v.findActionReceiverOrNull() - ?.pasteFiles(event.clipData, uri) - true - } + DragEvent.ACTION_DROP -> { + v.findActionReceiverOrNull() + ?.pasteFiles(event.clipData, uri) + true + } - else -> true + else -> true + } } - }) + } + ) } fun String?.valid() = this?.trim()?.isNotEmpty() == true - -fun format1024(args: Long): String { +suspend fun format1024(args: Long): String { if (args < 0) { return "Error" } val flags = arrayOf("B.", "KB", "MB", "GB", "TB") var flag = 0 var size = args.toDouble() - while (size >= pc_end_on) { - if (Thread.currentThread().isInterrupted) { - return "stopped" - } - size /= pc_end_on + while (size >= PC_END_ON) { + yield() + size /= PC_END_ON flag += 1 } assert(flag < flags.size) { @@ -361,7 +359,7 @@ data class FileExplorerSearch( ) fun fileSearchService( - database: LocalDatabase + database: AppDatabase ): suspend (searchQuery: FileExplorerSearch, start: Int, count: Int) -> SimpleResponse { return { searchQuery: FileExplorerSearch, start: Int, count: Int -> val listSafe = searchQuery.path.list() @@ -370,9 +368,11 @@ fun fileSearchService( val sortChains = searchQuery.sort val filterPredicate: (FileInfo) -> Boolean = { - (!searchQuery.filterHiddenFile || !it.kind.isHidden) && (filterList.isEmpty() || filterList.any { f -> - f.filter(it) - }) + (!searchQuery.filterHiddenFile || !it.kind.isHidden) && ( + filterList.isEmpty() || filterList.any { f -> + f.filter(it) + } + ) } val directories = listSafe.directories val files = listSafe.files @@ -384,12 +384,15 @@ fun fileSearchService( } val listFiles = if (searchQuery.filterHiddenFile || filterList.isNotEmpty()) { directories.filter(filterPredicate).plus(files.filter(filterPredicate)) - } else directories.plus(files) + } else { + directories.plus(files) + } val total = listFiles.size val index = start - 1 val startPosition = index * count - if (startPosition > total) SimpleResponse(0) - else { + if (startPosition > total) { + SimpleResponse(0) + } else { val items = listFiles .subList(startPosition, (startPosition + count).coerceAtMost(total)) .map { model -> @@ -401,14 +404,12 @@ fun fileSearchService( if (total > count * start) start + 1 else null ) } - } - } private suspend fun fileModelBuilder( model: FileInfo, - database: LocalDatabase + database: AppDatabase ): FileModel { val fileTime = model.time val lastModified = fileTime.lastModified ?: 0 @@ -416,22 +417,27 @@ private suspend fun fileModelBuilder( var torrentName = "" val kind = model.kind val length = if (kind is FileKind.File) { - database.mdDao().search(model.uri)?.let { - if (it.lastUpdateTime > lastModified) - md = it.data + database.mdDao().search(model.uri)?.takeIf { + it.lastUpdateTime > lastModified + }?.let { + md = it.data } - if (model.extension == "torrent") - database.torrentDao().search(model.uri)?.let { - if (it.lastUpdateTime > lastModified) - torrentName = it.torrent + if (model.extension == "torrent") { + database.torrentDao().search(model.uri)?.takeIf { + it.lastUpdateTime > lastModified + }?.let { + torrentName = it.torrent } + } kind.size } else { - //从数据库中查找 + // 从数据库中查找 val directory = database.sizeDao().search(model.uri) if (directory != null && directory.lastUpdateTime > lastModified) { directory.size - } else -1 + } else { + -1 + } } val fileKind = model.kind return FileModel( diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileListFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileListFragment.kt index 31cc840..3a1b4e2 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileListFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/FileListFragment.kt @@ -86,17 +86,23 @@ class SharePasteTargetViewModel : ViewModel() { class FileListFragment : SimpleFragment(FragmentFileListBinding::inflate) { private val fileOperateBinder get() = (requireContext() as MainActivity).fileOperateBinder - private val uuid by keyPrefix({ "uuid" }, avm({}) { - genericValueModel(UUID.randomUUID().toString()) - }) + private val uuid by keyPrefix( + { "uuid" }, + avm({}) { + genericValueModel(UUID.randomUUID().toString()) + } + ) private val args by navArgs() private val observer = FileListObserver(this, { args }, activityScope) - private val shareTarget by keyPrefix({ "shareTarget" }, pvm({}) { - SharePasteTargetViewModel() - }) + private val shareTarget by keyPrefix( + { "shareTarget" }, + pvm({}) { + SharePasteTargetViewModel() + } + ) override fun onBindViewEvent(binding: FragmentFileListBinding) { val adapter = SimpleSourceAdapter() @@ -131,34 +137,34 @@ class FileListFragment : SimpleFragment(FragmentFileLis private suspend fun openFolderInNewPage(holder: FileItemHolder) { val uri = observer.fileInstance?.toChild(holder.file.name, FileCreatePolicy.NotCreate)?.uri ?: return - startActivity(Intent(requireContext(), MainActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + activity?.newWindow { putExtra( "start", FileListFragmentArgs(uri).toBundle() ) - }) + } } private fun setupMenu() { - (requireActivity() as? MenuHost)?.addMenuProvider(object : MenuProvider { - override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - menuInflater.inflate(R.menu.file_list_menu, menu) - } - - override fun onPrepareMenu(menu: Menu) { - super.onPrepareMenu(menu) - updatePasteButtonState(menu) - } - - override fun onMenuItemSelected(menuItem: MenuItem) = when (menuItem.itemId) { - R.id.add_file -> addFile() - R.id.paste_file -> pasteFiles() - else -> false - } + (requireActivity() as? MenuHost)?.addMenuProvider( + object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.file_list_menu, menu) + } + override fun onPrepareMenu(menu: Menu) { + super.onPrepareMenu(menu) + updatePasteButtonState(menu) + } - }, owner) + override fun onMenuItemSelected(menuItem: MenuItem) = when (menuItem.itemId) { + R.id.add_file -> addFile() + R.id.paste_file -> pasteFiles() + else -> false + } + }, + owner + ) } private fun updatePasteButtonState(menu: Menu) { @@ -207,16 +213,15 @@ class FileListFragment : SimpleFragment(FragmentFileLis val uriList = resolveUri(data) if (uriList.any { it.scheme == ContentResolver.SCHEME_FILE && it == destDirectory - }) { - //静默处理 + } + ) { + // 静默处理 return@launch } if (uriList.isNotEmpty()) { startPasteFiles(uriList, dest, key) } - } - } private fun resolveUri(data: ClipData): List { @@ -255,8 +260,8 @@ class FileListFragment : SimpleFragment(FragmentFileLis getFileInstance(context, it).getFileInfo() } shareTarget.replace(uriList, dest) - val fileOperateBinderLocal = fileOperateBinder ?: kotlin.run { - Toast.makeText(requireContext(), "未连接服务", Toast.LENGTH_LONG).show() + val fileOperateBinderLocal = fileOperateBinder.value ?: kotlin.run { + Toast.makeText(context, "未连接服务", Toast.LENGTH_LONG).show() return } if (defaultSettings?.getBoolean("notify_before_paste", true) == true) { @@ -322,7 +327,7 @@ class FileListFragment : SimpleFragment(FragmentFileLis val ellipsizedText = TextUtils.ellipsize( fileName, TextPaint(), - 100f, + DEFAULT_OPEN_PROMPT_TITLE_WIDTH, TextUtils.TruncateAt.MIDDLE ) startActivity(Intent.createChooser(it, "open $ellipsizedText by")) @@ -339,7 +344,6 @@ class FileListFragment : SimpleFragment(FragmentFileLis ?: return@launch showMenu(view, fullPath, itemHolder, key, uri) } - } private fun showMenu( @@ -379,7 +383,7 @@ class FileListFragment : SimpleFragment(FragmentFileLis javaClass.classLoader?.loadClass("com.storyteller_f.li.plugin.LiPlugin") ?.getDeclaredConstructor() ?.newInstance() as? GiantExplorerShellPlugin - } catch (e: Exception) { + } catch (_: Exception) { null } ?: return val pluginManager = defaultPluginManager(key) @@ -394,7 +398,6 @@ class FileListFragment : SimpleFragment(FragmentFileLis return@setOnMenuItemClickListener true } } - } } @@ -404,7 +407,8 @@ class FileListFragment : SimpleFragment(FragmentFileLis val completableDeferred = CompletableDeferred() val requestPathDialogArgs = RequestPathDialog.bundle(requireContext()) request( - RequestPathDialog::class.java, requestPathDialogArgs + RequestPathDialog::class.java, + requestPathDialogArgs ).response( RequestPathDialog.RequestPathResult::class.java ) { result -> @@ -414,9 +418,8 @@ class FileListFragment : SimpleFragment(FragmentFileLis } override fun runInService(block: suspend GiantExplorerService.() -> Boolean) { - fileOperateBinder?.pluginTask(key, block) + fileOperateBinder.value?.pluginTask(key, block) } - } private fun showPropertiesDialog(fullPath: Uri) { @@ -430,7 +433,7 @@ class FileListFragment : SimpleFragment(FragmentFileLis itemHolder: FileItemHolder, key: String, ) { - fileOperateBinder?.delete( + fileOperateBinder.value?.delete( itemHolder.file.item, detectSelected(itemHolder), key @@ -445,13 +448,17 @@ class FileListFragment : SimpleFragment(FragmentFileLis val map = detectSelected(itemHolder).map { Uri.fromFile(File(it.fullPath)) } - val clipData = - ClipData.newPlainText(CLIP_DATA_KEY, map.first().toString()).apply { - if (map.size > 1) map.subList(1, map.size).forEach { - addItem(ClipData.Item(it)) - } + manager.setPrimaryClip(clipData(map)) + } + } + + private fun clipData(map: List): ClipData { + return ClipData.newPlainText(CLIP_DATA_KEY, map.first().toString()).apply { + if (map.size > 1) { + map.subList(1, map.size).forEach { + addItem(ClipData.Item(it)) } - manager.setPrimaryClip(clipData) + } } } @@ -476,22 +483,27 @@ class FileListFragment : SimpleFragment(FragmentFileLis fullPath: String, uri: Uri, ): Boolean { - if (pluginFile.name.endsWith("apk")) startActivity( - Intent( - requireContext(), - FragmentPluginActivity::class.java - ).apply { - putExtra("plugin-name", pluginFile.name) - plugUri(mimeTypeFromExtension, fullPath, uri) - }) - else startActivity( - Intent( - requireContext(), - WebViewPluginActivity::class.java - ).apply { - putExtra("plugin-name", pluginFile.name) - plugUri(mimeTypeFromExtension, fullPath, uri) - }) + if (pluginFile.name.endsWith("apk")) { + startActivity( + Intent( + requireContext(), + FragmentPluginActivity::class.java + ).apply { + putExtra("plugin-name", pluginFile.name) + plugUri(mimeTypeFromExtension, fullPath, uri) + } + ) + } else { + startActivity( + Intent( + requireContext(), + WebViewPluginActivity::class.java + ).apply { + putExtra("plugin-name", pluginFile.name) + plugUri(mimeTypeFromExtension, fullPath, uri) + } + ) + } return true } @@ -511,7 +523,6 @@ class FileListFragment : SimpleFragment(FragmentFileLis activities.forEach { addToMenu(it, intent) } - } private fun PopupMenu.addToMenu( @@ -519,8 +530,9 @@ class FileListFragment : SimpleFragment(FragmentFileLis intent: Intent, ) { val activityInfo = it.activityInfo ?: return - val groups = activityInfo.metaData?.getString("group")?.split("/") ?: return - val title = activityInfo.metaData?.getString("title") ?: return + val metaData = activityInfo.metaData ?: return + val groups = metaData.getString("group")?.split("/") ?: return + val title = metaData.getString("title") ?: return menu.loopAdd(groups).add(title).setOnMenuItemClickListener { intent.setPackage(requireContext().packageName).component = ComponentName(activityInfo.packageName, activityInfo.name) @@ -553,7 +565,7 @@ class FileListFragment : SimpleFragment(FragmentFileLis val key = uuid.data.value ?: return val detectSelected = detectSelected(itemHolder) Log.i(TAG, "moveOrCopy: uuid: $key") - fileOperateBinder?.moveOrCopy( + fileOperateBinder.value?.moveOrCopy( dest, detectSelected, itemHolder.file.item, @@ -565,6 +577,7 @@ class FileListFragment : SimpleFragment(FragmentFileLis companion object { const val CLIP_DATA_KEY = "file explorer" private const val TAG = "FileListFragment" + private const val DEFAULT_OPEN_PROMPT_TITLE_WIDTH = 100f } private fun detectSelected(itemHolder: FileItemHolder) = @@ -581,8 +594,9 @@ private fun Menu.loopAdd(strings: List): Menu { val subMenu = item?.subMenu if (item != null && subMenu != null) { subMenu - } else + } else { t.addSubMenu(e) + } } } @@ -592,4 +606,3 @@ private fun Intent.plugUri(mimeType: String?, fullPath: String, uri: Uri) { setDataAndType(build, mimeType) flags = Intent.FLAG_GRANT_READ_URI_PERMISSION } - diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/Functions.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/Functions.kt new file mode 100644 index 0000000..31daa2e --- /dev/null +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/Functions.kt @@ -0,0 +1,28 @@ +package com.storyteller_f.giant_explorer.control + +import android.app.Activity +import android.content.Intent +import android.os.Build +import com.storyteller_f.giant_explorer.R + +fun Activity.newWindow(extras: Intent.() -> Unit = {}) { + val openMode = defaultSettings.getString( + getString(R.string.setting_key_open_window_mode), + getString(R.string.default_open_window_mode) + ) + val base = Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_MULTIPLE_TASK + val flag = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + openMode == getString(R.string.adjacent_open_window_mode) + ) { + base or Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT + } else { + base + } + startActivity( + Intent(this, MainActivity::class.java).apply { + addFlags(flag) + extras() + } + ) +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/MainActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/MainActivity.kt index d1c31cb..7298724 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/MainActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/MainActivity.kt @@ -26,6 +26,7 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.switchMap import androidx.lifecycle.viewModelScope import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment @@ -37,6 +38,7 @@ import com.storyteller_f.common_ui.scope import com.storyteller_f.common_ui.setOnClick import com.storyteller_f.common_ui.supportNavigatorBarImmersive import com.storyteller_f.common_vm_ktx.StateValueModel +import com.storyteller_f.common_vm_ktx.debounce import com.storyteller_f.common_vm_ktx.svm import com.storyteller_f.common_vm_ktx.toDiffNoNull import com.storyteller_f.file_system.FileSystemUriStore @@ -46,6 +48,7 @@ import com.storyteller_f.file_system.instance.local.DocumentLocalFileInstance import com.storyteller_f.file_system.rawTree import com.storyteller_f.file_system_ktx.getFileInstance import com.storyteller_f.file_system_root.RootAccessFileInstance +import com.storyteller_f.giant_explorer.DEFAULT_DEBOUNCE import com.storyteller_f.giant_explorer.R import com.storyteller_f.giant_explorer.control.plugin.PluginManageActivity import com.storyteller_f.giant_explorer.control.remote.RemoteManagerActivity @@ -85,19 +88,20 @@ class FileExplorerSession(application: Application, uri: Uri) : AndroidViewModel } } +data class DocumentRequestSession(val authority: String, val tree: String?) + class MainActivity : CommonActivity(), FileOperateService.FileOperateResultContainer { private val binding by viewBinding(ActivityMainBinding::inflate) - private val filterHiddenFile by svm({}) { it, _ -> - StateValueModel(it, "filter-hidden-file", false) + private val filterHiddenFile by svm({}) { handle, _ -> + StateValueModel(handle, "filter-hidden-file", false) } private val fileListViewModel by svm({}) { handle, _ -> FileListViewModel(handle) } - private var currentRequestingAuthority: String? = null - private var currentRequestingTree: String? = null + private var currentRequesting: DocumentRequestSession? = null private val requestDocumentProvider = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { processDocumentProvider(it) @@ -121,8 +125,9 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeButtonEnabled(true) supportNavigatorBarImmersive(binding.content) + observeBinder() - //连接服务 + // 连接服务 val fileOperateIntent = Intent(this, FileOperateService::class.java) startService(fileOperateIntent) bindService(fileOperateIntent, connection, 0) @@ -130,10 +135,9 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta Shell.getShell { if (it.isRoot) { val intent = Intent(this, FileService::class.java) - //连接服务 + // 连接服务 RootService.bind(intent, fileConnection) } - } binding.drawer.addDrawerListener(drawableToggle) fileListViewModel.displayGrid.distinctUntilChanged().observe(owner) { @@ -156,18 +160,16 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta } private fun processDocumentProvider(uri: Uri?) { - val requestingAuthority = currentRequestingAuthority - val requestingTree = currentRequestingTree + val (requestingAuthority, requestingTree) = currentRequesting ?: return Log.i(TAG, "uri: $uri key: $requestingAuthority") - if (uri != null && requestingAuthority != null) { + if (uri != null) { val authority = uri.authority val tree = DocumentsContract.getTreeDocumentId(uri) if (requestingAuthority != authority || (requestingTree != null && requestingTree != tree)) { Toast.makeText(this, "选择错误", Toast.LENGTH_LONG).show() return } - currentRequestingAuthority = null - currentRequestingTree = null + this.currentRequesting = null saveUriAndSwitch(uri, tree, authority) menuProvider.flashFileSystemRootMenu() } @@ -175,7 +177,8 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta private fun saveUriAndSwitch(uri: Uri, tree: String, authority: String) { contentResolver.takePersistableUriPermission( - uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) FileSystemUriStore.instance.saveUri(this, authority, uri, tree) @@ -216,9 +219,12 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta val path = if (it == "/") "" else it val tree = uri.rawTree uri.buildUpon().path("/$tree$path").build() - } else uri.buildUpon().path(it).build() + } else { + uri.buildUpon().path(it).build() + } navController.navigate( - R.id.fileListFragment, FileListFragmentArgs(build).toBundle() + R.id.fileListFragment, + FileListFragmentArgs(build).toBundle() ) } } @@ -249,7 +255,8 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta R.id.volume_space -> request(VolumeSpaceDialog::class) R.id.background_task -> startActivity( Intent( - this, BackgroundTaskConfigActivity::class.java + this, + BackgroundTaskConfigActivity::class.java ) ) } @@ -267,26 +274,11 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta filterHiddenFile.data.value = newState } - private fun newWindow() { - val openMode = defaultSettings.getString( - getString(R.string.setting_key_open_window_mode), - getString(R.string.default_open_window_mode) - ) - val flag = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && openMode == getString(R.string.adjacent_open_window_mode)) { - Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT - } else { - Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_MULTIPLE_TASK - } - startActivity(Intent(this, MainActivity::class.java).apply { - addFlags(flag) - }) - } - private fun switchUriRoot(uri: Uri) { closeDrawer() findNavControl().navigate( - R.id.fileListFragment, FileListFragmentArgs(uri).toBundle() + R.id.fileListFragment, + FileListFragmentArgs(uri).toBundle() ) } @@ -298,8 +290,7 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta } } closeDrawer() - currentRequestingAuthority = authority - currentRequestingTree = tree + currentRequesting = DocumentRequestSession(authority, tree) val presetTreeKey = Properties().apply { load(assets.open("tree.keys")) @@ -312,7 +303,9 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta ) == null ) { DocumentsContract.buildRootUri(authority, presetTreeKey) - } else null + } else { + null + } requestDocumentProvider.launch(defaultTreeDocumentUri) return false } @@ -334,33 +327,49 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta ) } - var fileOperateBinder: FileOperateBinder? = null + val fileOperateBinder = MutableLiveData() + + private fun observeBinder() { + fileOperateBinder.switchMap { + it?.state?.toDiffNoNull { i, i2 -> + i == i2 + } + }.debounce(DEFAULT_DEBOUNCE).observe(this) { + if (it == null) { + Toast.makeText(this@MainActivity, "服务已关闭", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(this@MainActivity, "服务已连接", Toast.LENGTH_SHORT).show() + } + } + fileOperateBinder.observe(this) { binder -> + binder ?: return@observe + binder.fileOperateResultContainer = WeakReference(this@MainActivity) + binder.state.toDiffNoNull { i, i2 -> + i == i2 + }.observe(this@MainActivity) { + Toast.makeText( + this@MainActivity, + "${it.first} ${it.second}", + Toast.LENGTH_SHORT + ).show() + if (it.first == FileOperateBinder.state_null) { + FileOperationDialog().apply { + this.binder = binder + }.show(supportFragmentManager, FileOperationDialog.DIALOG_TAG) + } + } + } + } + private val connection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { - Toast.makeText(this@MainActivity, "服务已连接", Toast.LENGTH_SHORT).show() val fileOperateBinderLocal = service as FileOperateBinder Log.i(TAG, "onServiceConnected: $fileOperateBinderLocal") - fileOperateBinder = fileOperateBinderLocal - fileOperateBinderLocal.let { binder -> - binder.fileOperateResultContainer = WeakReference(this@MainActivity) - binder.state.toDiffNoNull { i, i2 -> - i == i2 - }.observe(this@MainActivity) { - Toast.makeText( - this@MainActivity, "${it.first} ${it.second}", Toast.LENGTH_SHORT - ).show() - if (it.first == FileOperateBinder.state_null) { - FileOperationDialog().apply { - this.binder = fileOperateBinderLocal - }.show(supportFragmentManager, FileOperationDialog.DIALOG_TAG) - } - } - } + fileOperateBinder.value = fileOperateBinderLocal } override fun onServiceDisconnected(name: ComponentName?) { - fileOperateBinder = null - Toast.makeText(this@MainActivity, "服务已关闭", Toast.LENGTH_SHORT).show() + fileOperateBinder.value = null } } @@ -415,7 +424,8 @@ class MainActivity : CommonActivity(), FileOperateService.FileOperateResultConta } fun PackageManager.queryIntentActivitiesCompat( - searchDocumentProvider: Intent, flags: Long + searchDocumentProvider: Intent, + flags: Long ): MutableList { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { queryIntentActivities(searchDocumentProvider, PackageManager.ResolveInfoFlags.of(flags)) @@ -425,11 +435,13 @@ fun PackageManager.queryIntentActivitiesCompat( } fun PackageManager.queryIntentContentProvidersCompat( - searchDocumentProvider: Intent, flags: Long + searchDocumentProvider: Intent, + flags: Long ): MutableList { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { queryIntentContentProviders( - searchDocumentProvider, PackageManager.ResolveInfoFlags.of(flags) + searchDocumentProvider, + PackageManager.ResolveInfoFlags.of(flags) ) } else { queryIntentContentProviders(searchDocumentProvider, flags.toInt()) @@ -441,12 +453,14 @@ suspend fun Activity.documentProviderRoot( tree: String, ): Uri? { val savedUris = FileSystemUriStore.instance.savedUris(this) - return if (!savedUris.contains(authority)) - null - else try { - val uri = DocumentLocalFileInstance.uriFromAuthority(authority, tree) - if (getFileInstance(this, uri).exists()) uri else null - } catch (e: Exception) { + return if (!savedUris.contains(authority)) { null + } else { + try { + val uri = DocumentLocalFileInstance.uriFromAuthority(authority, tree) + if (getFileInstance(this, uri).exists()) uri else null + } catch (_: Exception) { + null + } } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/SettingsActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/SettingsActivity.kt index 5f7a8db..8fdd6a4 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/SettingsActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/SettingsActivity.kt @@ -47,4 +47,4 @@ val Activity.defaultSettings: SharedPreferences get() = getSharedPreferences( "${packageName}_preferences", Activity.MODE_PRIVATE - ) \ No newline at end of file + ) diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/FragmentPluginActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/FragmentPluginActivity.kt index 729cf8e..c9ce563 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/FragmentPluginActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/FragmentPluginActivity.kt @@ -2,6 +2,7 @@ package com.storyteller_f.giant_explorer.control.plugin import android.content.Context import android.content.res.Resources +import android.net.Uri import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri @@ -19,7 +20,7 @@ import java.io.File import java.io.FileInputStream import java.io.FileOutputStream -const val giantExplorerPluginIni = "META-INF/giant-explorer-plugin.ini" +const val GIANT_EXPLORER_PLUGIN_INI = "META-INF/giant-explorer-plugin.ini" suspend fun Context.fileInputStream1(uriString: String) = getFileInstance(this, uriString.toUri()).apply { createFile() @@ -46,10 +47,11 @@ abstract class DefaultPluginManager(val context: Context) : GiantExplorerPluginM } } - override fun resolveParentUri(uriString: String): String? { - val uri = uriString.toUri() - val resolvePath = FileSystemProviderResolver.resolve(uri)?.path ?: return null - val parent = File(resolvePath).parent ?: return null + override fun resolveParentUri(uriString: String) = resolveParentUri(uriString.toUri()) + + private fun resolveParentUri(uri: Uri): String? { + val path = FileSystemProviderResolver.resolve(uri)?.path ?: return null + val parent = File(path).parent ?: return null return FileSystemProviderResolver.share(false, uri.buildUpon().path(parent).build()) .toString() } @@ -86,7 +88,9 @@ class FragmentPluginActivity : AppCompatActivity() { applicationInfo.publicSourceDir = absolutePath applicationInfo.sourceDir = absolutePath packageManager.getResourcesForApplication(applicationInfo) - } else null + } else { + null + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -99,10 +103,7 @@ class FragmentPluginActivity : AppCompatActivity() { return "" } - override fun runInService(block: suspend GiantExplorerService.() -> Boolean) { - - } - + override fun runInService(block: suspend GiantExplorerService.() -> Boolean) = Unit } lifecycleScope.launch { val revolvePlugin = pluginManagerRegister.resolvePluginName( @@ -135,9 +136,10 @@ class FragmentPluginActivity : AppCompatActivity() { val listOf = if (this::pluginFragments.isInitialized) pluginFragments else listOf() if (stackTrace.any { listOf.contains(it.className) - }) { + } + ) { return pluginResources!! } return super.getResources() } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginInfoFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginInfoFragment.kt index 57e95df..b3c793e 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginInfoFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginInfoFragment.kt @@ -33,8 +33,5 @@ class PluginInfoFragment : SimpleFragment(FragmentPlu } } - override fun onBindViewEvent(binding: FragmentPluginInfoBinding) { - - } - -} \ No newline at end of file + override fun onBindViewEvent(binding: FragmentPluginInfoBinding) = Unit +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginListFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginListFragment.kt index 513dab9..1ef8b5c 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginListFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginListFragment.kt @@ -27,17 +27,21 @@ class SimplePlugin(val path: String) : Model { class PluginListFragment : SimpleFragment(FragmentPluginListBinding::inflate) { private val adapter = SimpleSourceAdapter() - private val source by search(SearchProducer(service = { _, startPage, count -> - val toList = pluginManagerRegister.pluginsName().toList() - val startIndex = (startPage - 1) * count - val toIndex = (startIndex + count).coerceAtMost(toList.size) - val data = toList.subList(startIndex, toIndex).map { - SimplePlugin(it) - } - SimpleResponse(toList.size, data) - }, processFactory = { p, _, _ -> - PluginHolder(p.path) - })) + private val source by search( + SearchProducer(service = { _, startPage, count -> + val toList = pluginManagerRegister.pluginsName().toList() + val startIndex = (startPage - 1) * count + val toIndex = (startIndex + count).coerceAtMost(toList.size) + val data = toList.subList(startIndex, toIndex).map { + SimplePlugin(it) + } + SimpleResponse(toList.size, data) + }, processFactory = { p, _, _ -> + PluginHolder(p.path) + }) + ) + + override fun onBindViewEvent(binding: FragmentPluginListBinding) = Unit override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -48,25 +52,21 @@ class PluginListFragment : SimpleFragment(FragmentPlu source.observerInScope(this, "") { adapter.submitData(it) } - - } - - override fun onBindViewEvent(binding: FragmentPluginListBinding) { - } @BindClickEvent(PluginHolder::class) fun clickPlugin(itemHolder: PluginHolder) { - findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment, PluginInfoFragmentArgs(itemHolder.name).toBundle()) + findNavController().navigate( + R.id.action_FirstFragment_to_SecondFragment, + PluginInfoFragmentArgs(itemHolder.name).toBundle() + ) } - } class PluginHolder(val name: String) : DataItemHolder() { override fun areItemsTheSame(other: DataItemHolder): Boolean { return (other as PluginHolder).name == name } - } @BindItemHolder(PluginHolder::class) @@ -74,5 +74,4 @@ class PluginViewHolder(private val binding: ViewHolderPluginBinding) : BindingVi override fun bindData(itemHolder: PluginHolder) { binding.pluginName.text = itemHolder.name } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManageActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManageActivity.kt index 3b0346d..134bea6 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManageActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManageActivity.kt @@ -19,9 +19,7 @@ import com.storyteller_f.file_system_ktx.getFileInstance import com.storyteller_f.giant_explorer.R import com.storyteller_f.giant_explorer.databinding.ActivityPluginManageBinding import com.storyteller_f.giant_explorer.dialog.RequestPathDialog -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File @@ -57,7 +55,6 @@ class PluginManageActivity : CommonActivity() { lifecycleScope.launch { addPlugin(pluginFile, pluginRoot) } - } } } diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManager.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManager.kt index f191453..a34b742 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManager.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/PluginManager.kt @@ -20,7 +20,7 @@ object PluginType { const val html = 1 } -abstract class PluginConfiguration(val meta: PluginMeta): Model { +abstract class PluginConfiguration(val meta: PluginMeta) : Model { override fun commonId(): String { return meta.path } @@ -28,32 +28,51 @@ abstract class PluginConfiguration(val meta: PluginMeta): Model { data class PluginMeta(val version: String, val path: String, val name: String, val subMenu: String) -class FragmentPluginConfiguration(meta: PluginMeta, val classLoader: ClassLoader, val startFragment: String, val pluginFragments: List): PluginConfiguration(meta) { +class FragmentPluginConfiguration( + meta: PluginMeta, + val classLoader: ClassLoader, + val startFragment: String, + val pluginFragments: List +) : PluginConfiguration( + meta +) { companion object { fun resolve(meta: PluginMeta): FragmentPluginConfiguration { - val dexClassLoader = DexClassLoader(meta.path, null, null, meta.javaClass.classLoader) - val readText = dexClassLoader.getResourceAsStream(giantExplorerPluginIni).bufferedReader().readLines() + val classLoader = meta.javaClass.classLoader + val dexClassLoader = DexClassLoader(meta.path, null, null, classLoader) + val readText = + dexClassLoader.getResourceAsStream(GIANT_EXPLORER_PLUGIN_INI).bufferedReader() + .readLines() val startFragment = readText.first() val pluginFragments = readText[1].split(",") val version = readText.last() - return FragmentPluginConfiguration(meta.copy(version = version), dexClassLoader, startFragment, pluginFragments) + return FragmentPluginConfiguration( + meta.copy(version = version), + dexClassLoader, + startFragment, + pluginFragments + ) } } - } -class HtmlPluginConfiguration(meta: PluginMeta, val extractedPath: String) : PluginConfiguration(meta) { +class HtmlPluginConfiguration(meta: PluginMeta, val extractedPath: String) : + PluginConfiguration(meta) { companion object { suspend fun resolve(meta: PluginMeta, context: Context): HtmlPluginConfiguration { val pluginFile = File(meta.path) - val extractedPath = File(context.filesDir, "plugins/${pluginFile.nameWithoutExtension}").absolutePath + val extractedPath = + File(context.filesDir, "plugins/${pluginFile.nameWithoutExtension}").absolutePath File(meta.path).ensureExtract(extractedPath) val readText = File(extractedPath, "config").readText().split("\n") val version = readText.first() val subMenu = readText.lastOrNull() ?: "other" - return HtmlPluginConfiguration(meta.copy(version = version, subMenu = subMenu), extractedPath) + return HtmlPluginConfiguration( + meta.copy(version = version, subMenu = subMenu), + extractedPath + ) } } } @@ -91,11 +110,14 @@ class PluginManager { val pluginType = if (extension == "apk") PluginType.fragment else PluginType.html if (raw.contains(name)) raw.remove(name) val pluginMeta = PluginMeta("1.0", path, name, "other") - val configuration = if (pluginType == PluginType.fragment) FragmentPluginConfiguration.resolve( - pluginMeta - ) - else runBlocking { - HtmlPluginConfiguration.resolve(pluginMeta, context) + val configuration = if (pluginType == PluginType.fragment) { + FragmentPluginConfiguration.resolve( + pluginMeta + ) + } else { + runBlocking { + HtmlPluginConfiguration.resolve(pluginMeta, context) + } } map[name] = configuration return configuration @@ -156,8 +178,11 @@ object FileSystemProviderResolver { } fun share(encrypted: Boolean, uri: Uri): Uri? { - val authority = if (encrypted) BuildConfig.FILE_SYSTEM_ENCRYPTED_PROVIDER_AUTHORITY - else BuildConfig.FILE_SYSTEM_PROVIDER_AUTHORITY + val authority = if (encrypted) { + BuildConfig.FILE_SYSTEM_ENCRYPTED_PROVIDER_AUTHORITY + } else { + BuildConfig.FILE_SYSTEM_PROVIDER_AUTHORITY + } val (id, path) = if (uri.scheme == ContentResolver.SCHEME_CONTENT) { val rawTree = uri.rawTree val path = uri.path!!.substring(rawTree.length + 1) @@ -168,8 +193,9 @@ object FileSystemProviderResolver { val encodeByBase64 = id.encodeByBase64() val newPath = encodeByBase64 + path Log.d(TAG, "build: $newPath $encodeByBase64") - return Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority).path(newPath).build() + return Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) + .path(newPath).build() } private const val TAG = "FileSystemProviderReslv" -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/WebViewPluginActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/WebViewPluginActivity.kt index 050bdda..b1e1cb2 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/WebViewPluginActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/plugin/WebViewPluginActivity.kt @@ -31,7 +31,9 @@ class WebViewPluginActivity : AppCompatActivity() { private val messageChannel by lazy { if (WebViewFeature.isFeatureSupported(WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL)) { WebViewCompat.createWebMessageChannel(webView) - } else null + } else { + null + } } @SuppressLint("SetJavaScriptEnabled") @@ -43,7 +45,10 @@ class WebViewPluginActivity : AppCompatActivity() { messageChannel bindApi(webView, uriData) scope.launch { - val revolvePluginName = pluginManagerRegister.resolvePluginName(pluginName, this@WebViewPluginActivity) as HtmlPluginConfiguration + val revolvePluginName = pluginManagerRegister.resolvePluginName( + pluginName, + this@WebViewPluginActivity + ) as HtmlPluginConfiguration setupWebView(revolvePluginName.extractedPath) val indexFile = File(revolvePluginName.extractedPath, "index.html") val toString = Uri.fromFile(indexFile).toString() @@ -55,44 +60,88 @@ class WebViewPluginActivity : AppCompatActivity() { @SuppressLint("SetJavaScriptEnabled") private fun setupWebView(extractedPath: String) { if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { - WebViewCompat.addWebMessageListener(webView, "test", setOf(BASE_URL)) { view, message, sourceOrigin, isMainFrame, replyProxy -> - Log.d(TAG, "onCreate() called with: view = $view, message = ${message.data}, sourceOrigin = $sourceOrigin, isMainFrame = $isMainFrame, replyProxy = $replyProxy") + WebViewCompat.addWebMessageListener( + webView, + "test", + setOf(BASE_URL) + ) { view, message, sourceOrigin, isMainFrame, replyProxy -> + Log.d( + TAG, + "onCreate() called with: view = $view, " + + "message = ${message.data}, " + + "sourceOrigin = $sourceOrigin, " + + "isMainFrame = $isMainFrame, " + + "replyProxy = $replyProxy" + ) replyProxy.postMessage("from android") } } webView.webViewClient = object : WebViewClient() { - override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { + override fun shouldOverrideUrlLoading( + view: WebView?, + request: WebResourceRequest? + ): Boolean { Log.i(TAG, "shouldOverrideUrlLoading: ${request?.url}") return super.shouldOverrideUrlLoading(view, request) } - override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? { + override fun shouldInterceptRequest( + view: WebView?, + request: WebResourceRequest? + ): WebResourceResponse? { Log.d(TAG, "shouldInterceptRequest() called with: request = ${request?.url}") if (request != null) { val url = request.url val path = url.path if (url.toString().startsWith(BASE_URL) && path?.endsWith(".js") == true) { - return WebResourceResponse("text/javascript", "utf-8", FileInputStream(File(extractedPath, path))) + return WebResourceResponse( + "text/javascript", + "utf-8", + FileInputStream(File(extractedPath, path)) + ) } } return super.shouldInterceptRequest(view, request) } - override fun onReceivedHttpError(view: WebView?, request: WebResourceRequest?, errorResponse: WebResourceResponse?) { - Log.d(TAG, "onReceivedHttpError() called with: view = $view, request = ${request?.url}, errorResponse = ${errorResponse?.reasonPhrase}") + override fun onReceivedHttpError( + view: WebView?, + request: WebResourceRequest?, + errorResponse: WebResourceResponse? + ) { + Log.d( + TAG, + "onReceivedHttpError() called with: view = $view, request = ${request?.url}, " + + "errorResponse = ${errorResponse?.reasonPhrase}" + ) super.onReceivedHttpError(view, request, errorResponse) } - override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) { + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError? + ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - Log.d(TAG, "onReceivedError() called with: view = $view, request = ${request?.url}, error = ${error?.description} ${error?.errorCode}") + Log.d( + TAG, + "onReceivedError() called with: view = $view, request = ${request?.url}, " + + "error = ${error?.description} ${error?.errorCode}" + ) } super.onReceivedError(view, request, error) } - override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) { - Log.d(TAG, "onReceivedSslError() called with: view = $view, handler = $handler, error = $error") + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError? + ) { + Log.d( + TAG, + "onReceivedSslError() called with: view = $view, handler = $handler, error = $error" + ) super.onReceivedSslError(view, handler, error) } } @@ -122,14 +171,16 @@ class WebViewPluginActivity : AppCompatActivity() { messageChannel?.let { if (WebViewFeature.isFeatureSupported(WebViewFeature.POST_WEB_MESSAGE)) { val webMessageCompat = WebMessageCompat(result, it) - WebViewCompat.postWebMessage(webView, webMessageCompat, Uri.parse(BASE_URL)) + WebViewCompat.postWebMessage( + webView, + webMessageCompat, + Uri.parse(BASE_URL) + ) } } } } - } - } val f = object : WebViewFilePlugin { @JavascriptInterface @@ -150,9 +201,7 @@ class WebViewPluginActivity : AppCompatActivity() { companion object { private const val TAG = "WebViewPluginActivity" const val BASE_URL = "http://www.example.com" - } - } interface WebViewFilePlugin { @@ -167,7 +216,7 @@ fun WebView.callback(callbackId: String, parameters: String?) { suspend fun File.ensureExtract(extracted: String) { withContext(Dispatchers.IO) { ZipInputStream(FileInputStream(this@ensureExtract)).use { - val byteArray = ByteArray(1024) + val byteArray = ByteArray(DEFAULT_BUFFER_SIZE) while (true) { val nextEntry = it.nextEntry if (nextEntry != null) { @@ -178,11 +227,15 @@ suspend fun File.ensureExtract(extracted: String) { val read = it.read(byteArray) if (read != -1) { out.write(byteArray, 0, read) - } else break + } else { + break + } } } - } else break + } else { + break + } } } } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteDetailFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteDetailFragment.kt index 134fe1d..023d9d8 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteDetailFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteDetailFragment.kt @@ -46,9 +46,10 @@ class RemoteDetailFragment : Fragment() { extrasProducer = { buildExtras { } - }) + } + ) - //is smb + // is smb private val mode by vm({}) { GenericValueModel().apply { data.value = RemoteAccessType.SMB @@ -56,13 +57,12 @@ class RemoteDetailFragment : Fragment() { } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentRemoteDetailBinding.inflate(inflater, container, false) return binding.root - } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -92,31 +92,38 @@ class RemoteDetailFragment : Fragment() { } } binding.testConnection.setOnClick { - scope.launch { - waitingDialog { - withContext(Dispatchers.IO) { - when (mode.data.value) { - RemoteAccessType.SMB -> shareSpec().checkSmb() - RemoteAccessType.FTP -> FtpInstance(spec()).open() - RemoteAccessType.FTP_ES -> FtpsInstance(spec()).open() - RemoteAccessType.FTPS -> FtpsInstance(spec()).open() - RemoteAccessType.WEB_DAV -> WebDavInstance(shareSpec()).list("/") - else -> spec().checkSftp() - } - } - - Toast.makeText(requireContext(), "success", Toast.LENGTH_SHORT).show() - } + testConnection() + } + binding.buttonOk.setOnClickListener { + save() + } + } + private fun save() { + scope.launch { + val isSmb = mode.data.value == RemoteAccessType.SMB + withContext(Dispatchers.IO) { + val dao = requireDatabase.remoteAccessDao() + if (isSmb) dao.add(shareSpec().toRemote()) else dao.add(spec().toRemote()) } + Toast.makeText(requireContext(), "success", Toast.LENGTH_SHORT).show() } - binding.buttonSecond.setOnClickListener { - scope.launch { - val isSmb = mode.data.value == RemoteAccessType.SMB + } + + private fun testConnection() { + scope.launch { + waitingDialog { withContext(Dispatchers.IO) { - val dao = requireDatabase.remoteAccessDao() - if (isSmb) dao.add(shareSpec().toRemote()) else dao.add(spec().toRemote()) + when (mode.data.value) { + RemoteAccessType.SMB -> shareSpec().checkSmb() + RemoteAccessType.FTP -> FtpInstance(spec()).open() + RemoteAccessType.FTP_ES -> FtpsInstance(spec()).open() + RemoteAccessType.FTPS -> FtpsInstance(spec()).open() + RemoteAccessType.WEB_DAV -> WebDavInstance(shareSpec()).list("/") + else -> spec().checkSftp() + } } + Toast.makeText(requireContext(), "success", Toast.LENGTH_SHORT).show() } } @@ -147,4 +154,4 @@ class RemoteDetailFragment : Fragment() { super.onDestroyView() _binding = null } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteListFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteListFragment.kt index d344579..826d057 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteListFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteListFragment.kt @@ -35,13 +35,12 @@ class RemoteListFragment : Fragment() { private val binding get() = _binding!! override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentRemoteListBinding.inflate(inflater, container, false) return binding.root - } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -53,10 +52,14 @@ class RemoteListFragment : Fragment() { .flowWithLifecycle(viewLifecycleOwner.lifecycle) .shareIn(scope, SharingStarted.WhileSubscribed()) .collectLatest { - binding.content.flash(ListWithState.UIState(false, it.isNotEmpty(), empty = false, progress = false, null, null)) - adapter.submitList(it.map { spec -> - RemoteAccessSpecHolder(spec) - }) + binding.content.flash( + ListWithState.UIState(false, it.isNotEmpty(), empty = false, progress = false, null, null) + ) + adapter.submitList( + it.map { spec -> + RemoteAccessSpecHolder(spec) + } + ) } } } @@ -76,10 +79,12 @@ class RemoteAccessSpecHolder(val spec: RemoteAccessSpec) : DataItemHolder() { } @BindItemHolder(RemoteAccessSpecHolder::class) -class RemoteAccessSpecViewHolder(private val binding: ViewHolderRemoteAccessSpecBinding) : BindingViewHolder(binding) { +class RemoteAccessSpecViewHolder( + private val binding: ViewHolderRemoteAccessSpecBinding +) : BindingViewHolder( + binding +) { override fun bindData(itemHolder: RemoteAccessSpecHolder) { binding.url.text = itemHolder.spec.toFtpSpec().toUri().toString() - } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteManagerActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteManagerActivity.kt index 0853a88..c3b80fd 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteManagerActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/remote/RemoteManagerActivity.kt @@ -1,7 +1,6 @@ package com.storyteller_f.giant_explorer.control.remote import android.os.Bundle -import com.google.android.material.snackbar.Snackbar import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.navigation.findNavController @@ -36,7 +35,7 @@ class RemoteManagerActivity : AppCompatActivity() { override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.nav_host_fragment_content_remote_manager) - return navController.navigateUp(appBarConfiguration) - || super.onSupportNavigateUp() + return navController.navigateUp(appBarConfiguration) || + super.onSupportNavigateUp() } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessActivity.kt index ee83d26..95851c8 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessActivity.kt @@ -32,15 +32,13 @@ class RootAccessActivity : AppCompatActivity() { val warmup = client.warmup(0) Log.i(TAG, "onCustomTabsServiceConnected: warmup $warmup") newSession = client.newSession(object : CustomTabsCallback() { - }) - newSession?.mayLaunchUrl(Uri.parse(MagiskUrl), null, null) - newSession?.mayLaunchUrl(Uri.parse(kernelSuUrl), null, null) + newSession?.mayLaunchUrl(Uri.parse(MAGISK_URL), null, null) + newSession?.mayLaunchUrl(Uri.parse(KERNEL_SU_URL), null, null) } - } - override fun onServiceDisconnected(name: ComponentName) {} + override fun onServiceDisconnected(name: ComponentName) = Unit } override fun onCreate(savedInstanceState: Bundle?) { @@ -72,12 +70,12 @@ class RootAccessActivity : AppCompatActivity() { override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.nav_host_fragment_content_root_access) - return navController.navigateUp(appBarConfiguration) - || super.onSupportNavigateUp() + return navController.navigateUp(appBarConfiguration) || + super.onSupportNavigateUp() } companion object { private const val TAG = "RootAccessActivity" private const val CUSTOM_TAB_PACKAGE_NAME = "com.android.chrome" // Change when in stable } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessStatusFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessStatusFragment.kt index d7b3c1e..cc6313f 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessStatusFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootAccessStatusFragment.kt @@ -13,7 +13,9 @@ import com.storyteller_f.giant_explorer.R import com.storyteller_f.giant_explorer.databinding.FragmentRootAccessStatusBinding import com.topjohnwu.superuser.Shell -class RootAccessStatusFragment : SimpleFragment(FragmentRootAccessStatusBinding::inflate) { +class RootAccessStatusFragment : SimpleFragment( + FragmentRootAccessStatusBinding::inflate +) { private val state by svm({}) { handle, _ -> StateValueModel(handle, default = false) @@ -37,4 +39,4 @@ class RootAccessStatusFragment : SimpleFragment binding.status.isEnabled = it } } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootIntroFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootIntroFragment.kt index cc1ac12..e6b6c61 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootIntroFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/root/RootIntroFragment.kt @@ -12,38 +12,40 @@ import androidx.annotation.RequiresApi import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat import com.storyteller_f.common_ui.SimpleFragment +import com.storyteller_f.giant_explorer.DEFAULT_WEBVIEW_HEIGHT import com.storyteller_f.giant_explorer.databinding.FragmentRootIntroBinding -const val MagiskUrl = "https://github.com/topjohnwu/Magisk" -const val kernelSuUrl = "https://github.com/tiann/KernelSU" +const val MAGISK_URL = "https://github.com/topjohnwu/Magisk" +const val KERNEL_SU_URL = "https://github.com/tiann/KernelSU" class RootIntroFragment : SimpleFragment(FragmentRootIntroBinding::inflate) { override fun onBindViewEvent(binding: FragmentRootIntroBinding) { - binding.button.setOnClickListener { - open(MagiskUrl) + open(MAGISK_URL) } binding.kernelSu.setOnClickListener { - open(kernelSuUrl) + open(KERNEL_SU_URL) } } private fun open(url: String) { val newSession = (requireActivity() as RootAccessActivity).newSession val height = ScreenMetricsCompat.getScreenSize(requireContext()).height - val builder = CustomTabsIntent.Builder().setInitialActivityHeightPx((height * 0.7).toInt()) + val builder = CustomTabsIntent.Builder().setInitialActivityHeightPx((height * DEFAULT_WEBVIEW_HEIGHT).toInt()) if (newSession != null) builder.setSession(newSession) val customTabsIntent = builder.build() customTabsIntent.launchUrl(requireContext(), Uri.parse(url)) } - } object ScreenMetricsCompat { private val api: Api = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ApiLevel30() - else Api() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ApiLevel30() + } else { + Api() + } /** * Returns screen size in pixels. @@ -70,4 +72,4 @@ object ScreenMetricsCompat { return Size(metrics.bounds.width(), metrics.bounds.height()) } } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/AddTaskFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/AddTaskFragment.kt index cf02017..d4601d6 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/AddTaskFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/AddTaskFragment.kt @@ -21,11 +21,17 @@ class AddTaskFragment : SimpleFragment(FragmentAddTaskBi override fun onBindViewEvent(binding: FragmentAddTaskBinding) { binding.button.setOnClickListener { - val result = Result(binding.checkBox2.isChecked, binding.path.text.toString().toUri(), binding.selectWorkerName.selectedItem.toString()) + val result = + Result( + binding.checkBox2.isChecked, + binding.path.text.toString().toUri(), + binding.selectWorkerName.selectedItem.toString() + ) scope.launch { val waitingDialog = waitingDialog() try { - requireDatabase.bigTimeDao().add(BigTimeTask(result.uri, result.enable, result.category)) + requireDatabase.bigTimeDao() + .add(BigTimeTask(result.uri, result.enable, result.category)) setFragmentResult(result) findNavController().navigateUp() } catch (e: Exception) { @@ -34,9 +40,12 @@ class AddTaskFragment : SimpleFragment(FragmentAddTaskBi waitingDialog.end() } } - } - binding.selectWorkerName.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, arrayOf("message digest", "torrent name", "folder size")) + binding.selectWorkerName.adapter = ArrayAdapter( + requireContext(), + android.R.layout.simple_spinner_dropdown_item, + arrayOf("message digest", "torrent name", "folder size") + ) binding.selectPath.setOnClick { findNavController().request(R.id.action_select_task_path) .response(RequestPathDialog.RequestPathResult::class.java) { result -> @@ -48,5 +57,4 @@ class AddTaskFragment : SimpleFragment(FragmentAddTaskBi @Parcelize class Result(val enable: Boolean, val uri: Uri, val category: String) : Parcelable companion object - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskConfigActivity.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskConfigActivity.kt index 3370450..2616e3b 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskConfigActivity.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskConfigActivity.kt @@ -33,7 +33,7 @@ class BackgroundTaskConfigActivity : AppCompatActivity() { override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.nav_host_fragment_content_background_task_config) - return navController.navigateUp(appBarConfiguration) - || super.onSupportNavigateUp() + return navController.navigateUp(appBarConfiguration) || + super.onSupportNavigateUp() } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskListFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskListFragment.kt index e5ac3c7..40bc473 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskListFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/control/task/BackgroundTaskListFragment.kt @@ -49,13 +49,17 @@ class BackgroundTaskListFragment : SimpleFragment(Fragm .flowWithLifecycle(viewLifecycleOwner.lifecycle) .shareIn(scope, SharingStarted.WhileSubscribed()) .collectLatest { - binding.content.flash(ListWithState.UIState(false, it.isNotEmpty(), empty = false, progress = false, null, null)) + binding.content.flash( + ListWithState.UIState(false, it.isNotEmpty(), empty = false, progress = false, null, null) + ) val list = mutableListOf() it.forEach { (category, result) -> list.add(TaskTypeHolder(category)) - list.addAll(result.map { task -> - BigTimeTaskItemHolder(task) - }) + list.addAll( + result.map { task -> + BigTimeTaskItemHolder(task) + } + ) } adapter.submitList(list) } @@ -68,7 +72,13 @@ class BackgroundTaskListFragment : SimpleFragment(Fragm val waitingDialog = waitingDialog() try { withContext(Dispatchers.IO) { - requireDatabase.bigTimeDao().update(BigTimeTask(itemHolder.bigTimeWorker.uri, !itemHolder.bigTimeWorker.enable, itemHolder.bigTimeWorker.category)) + requireDatabase.bigTimeDao().update( + BigTimeTask( + itemHolder.bigTimeWorker.uri, + !itemHolder.bigTimeWorker.enable, + itemHolder.bigTimeWorker.category + ) + ) } } catch (e: Exception) { Toast.makeText(requireContext(), e.exceptionMessage, Toast.LENGTH_SHORT).show() @@ -92,7 +102,6 @@ class TaskTypeViewHolder(edComposeView: EDComposeView) : ComposeViewHolder { @@ -126,7 +135,6 @@ class BigTimeTaskViewHolder(edComposeView: EDComposeView) : ComposeViewHolder { @@ -134,12 +142,14 @@ class BigTimeTaskProvider : PreviewParameterProvider { get() = sequence { yield(BigTimeTaskItemHolder(BigTimeTask(File("/storage/emulated/0/Download").toUri(), true, "md"))) } - } @Preview @Composable -fun BigTimeTaskView(@PreviewParameter(BigTimeTaskProvider::class) itemHolder: BigTimeTaskItemHolder, edComposeView: EdComposeViewEventEmitter = EdComposeViewEventEmitter.default) { +fun BigTimeTaskView( + @PreviewParameter(BigTimeTaskProvider::class) itemHolder: BigTimeTaskItemHolder, + edComposeView: EdComposeViewEventEmitter = EdComposeViewEventEmitter.default +) { Row( modifier = Modifier .fillMaxWidth() @@ -147,6 +157,9 @@ fun BigTimeTaskView(@PreviewParameter(BigTimeTaskProvider::class) itemHolder: Bi verticalAlignment = Alignment.CenterVertically ) { Text(text = itemHolder.bigTimeWorker.uri.toString(), modifier = Modifier.weight(1f)) - Checkbox(checked = itemHolder.bigTimeWorker.enable, onCheckedChange = { edComposeView.notifyClickEvent("check") }) + Checkbox( + checked = itemHolder.bigTimeWorker.enable, + onCheckedChange = { edComposeView.notifyClickEvent("check") } + ) } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/AppDatabase.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/AppDatabase.kt index a6de3cf..ff8d5ee 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/AppDatabase.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/AppDatabase.kt @@ -9,12 +9,16 @@ import com.storyteller_f.ext_func_definition.ExtFuncFlat import com.storyteller_f.ext_func_definition.ExtFuncFlatType @Database( - entities = [FileSizeRecord::class, FileMDRecord::class, FileTorrentRecord::class, BigTimeTask::class, RemoteAccessSpec::class], + entities = [FileSizeRecord::class, + FileMDRecord::class, + FileTorrentRecord::class, + BigTimeTask::class, + RemoteAccessSpec::class], version = 1, exportSchema = true, ) @TypeConverters(Converters::class) -abstract class LocalDatabase : RoomDatabase() { +abstract class AppDatabase : RoomDatabase() { abstract fun sizeDao(): FileSizeRecordDao abstract fun mdDao(): FileMDRecordDao @@ -25,9 +29,9 @@ abstract class LocalDatabase : RoomDatabase() { companion object { @Volatile - private var INSTANCE: LocalDatabase? = null + private var INSTANCE: AppDatabase? = null - fun getInstance(context: Context): LocalDatabase = + fun getInstance(context: Context): AppDatabase = INSTANCE ?: synchronized(this) { INSTANCE ?: buildDatabase(context).also { INSTANCE = it } } @@ -35,7 +39,8 @@ abstract class LocalDatabase : RoomDatabase() { private fun buildDatabase(context: Context) = Room.databaseBuilder( context.applicationContext, - LocalDatabase::class.java, "file-record.db" + AppDatabase::class.java, + "file-record.db" ) .fallbackToDestructiveMigration() .build() @@ -44,4 +49,4 @@ abstract class LocalDatabase : RoomDatabase() { @ExtFuncFlat(type = ExtFuncFlatType.V2) val Context.requireDatabase - get() = LocalDatabase.getInstance(this) + get() = AppDatabase.getInstance(this) diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/RemoteAccessSpec.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/RemoteAccessSpec.kt index 2f1c895..ef1346f 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/RemoteAccessSpec.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/database/RemoteAccessSpec.kt @@ -52,4 +52,4 @@ fun ShareSpec.toRemote() = RemoteAccessSpec(server, port, user, password, share, type) fun RemoteSpec.toRemote() = - RemoteAccessSpec(server, port, user, password, type = type) \ No newline at end of file + RemoteAccessSpec(server, port, user, password, type = type) diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FileOperationDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FileOperationDialog.kt index 3fea861..db02cfc 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FileOperationDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FileOperationDialog.kt @@ -18,41 +18,56 @@ import com.storyteller_f.common_vm_ktx.keyPrefix import com.storyteller_f.common_vm_ktx.to import com.storyteller_f.common_vm_ktx.vm import com.storyteller_f.file_system.operate.DefaultForemanProgressAdapter +import com.storyteller_f.giant_explorer.DEFAULT_DEBOUNCE import com.storyteller_f.giant_explorer.R import com.storyteller_f.giant_explorer.databinding.DialogFileOperationBinding import com.storyteller_f.giant_explorer.service.FileOperateBinder import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow +import java.util.Locale import java.util.UUID class FileOperationDialog : SimpleDialogFragment(DialogFileOperationBinding::inflate) { lateinit var binder: FileOperateBinder - private val progressVM by keyPrefix({ "progress" }, vm({}) { - GenericValueModel() - }) - private val leftVM by keyPrefix("left", vm({}) { - GenericValueModel>().apply { - data.value = -1 to -1 to -1 + private val progressVM by keyPrefix( + { "progress" }, + vm({}) { + GenericValueModel() } - }) - private val stateVM by keyPrefix("state", vm({}) { - GenericValueModel() - }) - private val tipVM by keyPrefix("tip", vm({}) { - GenericValueModel() - }) - private val uuid by keyPrefix({ "uuid" }, avm({}) { - GenericValueModel().apply { - Log.i(TAG, "uuid: new") - data.value = UUID.randomUUID().toString() + ) + private val leftVM by keyPrefix( + "left", + vm({}) { + GenericValueModel>().apply { + data.value = -1 to -1 to -1 + } } - }) - - override fun onBindViewEvent(binding: DialogFileOperationBinding) { + ) + private val stateVM by keyPrefix( + "state", + vm({}) { + GenericValueModel() + } + ) + private val tipVM by keyPrefix( + "tip", + vm({}) { + GenericValueModel() + } + ) + private val uuid by keyPrefix( + { "uuid" }, + avm({}) { + GenericValueModel().apply { + Log.i(TAG, "uuid: new") + data.value = UUID.randomUUID().toString() + } + } + ) - } + override fun onBindViewEvent(binding: DialogFileOperationBinding) = Unit override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -70,7 +85,9 @@ class FileOperationDialog : if (!binder.map.containsKey(key)) dismiss() bindUi(key, list, binding) bindListener(key, binding) - } else dismiss() + } else { + dismiss() + } } private fun bindListener(key: String, binding: DialogFileOperationBinding) { @@ -91,7 +108,6 @@ class FileOperationDialog : it.isVisible = true } } - }) val callbackFlow = callbackFlow { val defaultProgressListener = object : DefaultForemanProgressAdapter() { @@ -111,13 +127,14 @@ class FileOperationDialog : } private fun bindUi(key: String, list: List, binding: DialogFileOperationBinding) { - binder.state.debounce(500).distinctUntilChanged().observe(owner) { + binder.state.distinctUntilChanged().debounce(DEFAULT_DEBOUNCE).observe(owner) { when (it) { FileOperateBinder.state_running -> { val task = binder.map[key]?.taskAssessResult list.onVisible(binding.stateRunning) Log.i(TAG, "onBindViewEvent: $key $task ${binder.map.keys}") binding.textViewTask.text = String.format( + Locale.getDefault(), "total size: %d\ntotal file:%d\ntotal folder:%d", task?.size, task?.fileCount, @@ -155,7 +172,7 @@ class FileOperationDialog : } private fun presentTaskSnapshot(it: Triple) = - String.format("size: %d\nleft file:%d\nleft folder:%d", it.third, it.first, it.second) + String.format(Locale.getDefault(), "size: %d\nleft file:%d\nleft folder:%d", it.third, it.first, it.second) override fun onDestroyView() { super.onDestroyView() @@ -170,4 +187,4 @@ class FileOperationDialog : interface Handler { fun close() } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FilterDialogFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FilterDialogFragment.kt index 845d9be..68846fa 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FilterDialogFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/FilterDialogFragment.kt @@ -41,8 +41,7 @@ class FilterDialogFragment : CommonDialogFragment() { activeFilters.value = activeList } - override fun onEditingChanged(editing: List>) { - } + override fun onEditingChanged(editing: List>) = Unit override fun onRestoreState(configItems: List): List> { return configItems.buildFilters() @@ -57,7 +56,8 @@ class FilterDialogFragment : CommonDialogFragment() { companion object { const val SUFFIX = "filter" val factory: RuntimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of( - ConfigItem::class.java, "config-item-key" + ConfigItem::class.java, + "config-item-key" ).registerSubtype(NameFilter.Config::class.java, "name")!! } } diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/NewNameDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/NewNameDialog.kt index 9378398..7793aa4 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/NewNameDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/NewNameDialog.kt @@ -24,4 +24,4 @@ class NewNameDialog : SimpleDialogFragment(DialogNewNameBi companion object { const val requestKey = "add-file" } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/OpenFileDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/OpenFileDialog.kt index 86765f5..6f70d63 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/OpenFileDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/OpenFileDialog.kt @@ -13,6 +13,7 @@ import com.storyteller_f.common_vm_ktx.GenericValueModel import com.storyteller_f.common_vm_ktx.vm import com.storyteller_f.file_system.util.getExtension import com.storyteller_f.file_system_ktx.getFileInstance +import com.storyteller_f.giant_explorer.DEFAULT_DEBOUNCE import com.storyteller_f.giant_explorer.databinding.DialogOpenFileBinding import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -49,7 +50,7 @@ class OpenFileDialog : SimpleDialogFragment(DialogOpenFil .getMimeTypeFromExtension(getExtension(uri.path!!)) binding.mimeType = mimeTypeFromExtension scope.launch { - delay(100) + delay(DEFAULT_DEBOUNCE) dataType.data.value = ContentInfoUtil().findMatch(fileInstance.getFileInputStream().buffered()) } @@ -60,20 +61,24 @@ class OpenFileDialog : SimpleDialogFragment(DialogOpenFil binding.openByVideo.setBackgroundColor(mixColor(mimeTypeFromExtension, it, "video")) binding.openByHex.setBackgroundColor(mixColor(mimeTypeFromExtension, it, "application")) } - } private fun mixColor(mimeTypeFromExtension: String?, contentInfo: ContentInfo?, t: String): Int { - val fromMagicNumber = if (contentInfo?.contentType?.mimeType?.contains(t) == true) 1 else 2 - val fromName = if (mimeTypeFromExtension?.contains(t) == true) 4 else 8 + val fromMagicNumber = if (contentInfo?.contentType?.mimeType?.contains(t) == true) MAGIC_TARGET else 0 + val fromName = if (mimeTypeFromExtension?.contains(t) == true) EXTENSION_TARGET else 0 return (fromMagicNumber + fromName).let { when (it) { - 9 -> Color.parseColor("#A25B32") - 6 -> Color.parseColor("#667DDA") - 5 -> Color.parseColor("#D2D205") + MAGIC_TARGET -> Color.parseColor("#A25B32") + EXTENSION_TARGET -> Color.parseColor("#667DDA") + MIX_TARGET -> Color.parseColor("#D2D205") else -> Color.GRAY } } } -} + companion object { + const val MAGIC_TARGET = 1 + const val EXTENSION_TARGET = 2 + const val MIX_TARGET = 3 + } +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/PropertiesDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/PropertiesDialog.kt index 8d05cc9..05d27c7 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/PropertiesDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/PropertiesDialog.kt @@ -27,7 +27,13 @@ import kotlinx.coroutines.launch class PropertiesDialog : SimpleDialogFragment(DialogFilePropertiesBinding::inflate) { private val args by navArgs() override fun onBindViewEvent(binding: DialogFilePropertiesBinding) { - listOf(binding.name, binding.fullPath, binding.accessedTime, binding.modifiedTime, binding.createdTime).forEach { + listOf( + binding.name, + binding.fullPath, + binding.accessedTime, + binding.modifiedTime, + binding.createdTime + ).forEach { it.copyTextFeature() } } @@ -38,8 +44,11 @@ class PropertiesDialog : SimpleDialogFragment(Dialo val fileInstance = getFileInstance(requireContext(), args.uri) scope.launch { val fileKind = fileInstance.fileKind() - val length = if (fileKind.isFile) fileInstance.getFileLength() - else 0 + val length = if (fileKind.isFile) { + fileInstance.getFileLength() + } else { + 0 + } val model = fileInstance.getFileInfo() binding.model = FileModel( model, @@ -63,7 +72,6 @@ class PropertiesDialog : SimpleDialogFragment(Dialo binding.audioInfo.text = getString(R.string.duration_ms, duration) } } - } private fun videoInfo(fileInstance: FileInstance): String { @@ -88,7 +96,7 @@ class PropertiesDialog : SimpleDialogFragment(Dialo sample rate: $sampleRate frame rate: $frameRate color format: $colorFormat - """.trimIndent() + """.trimIndent() mediaExtractor.release() return trimIndent } @@ -101,7 +109,10 @@ class PropertiesDialog : SimpleDialogFragment(Dialo } fun Context.copyText(text: CharSequence) { - ContextCompat.getSystemService(this, ClipboardManager::class.java)?.setPrimaryClip(ClipData.newPlainText("text", text)) + ContextCompat.getSystemService( + this, + ClipboardManager::class.java + )?.setPrimaryClip(ClipData.newPlainText("text", text)) Toast.makeText(this, "copied", Toast.LENGTH_SHORT).show() } @@ -119,4 +130,4 @@ fun TextView.copyTextFeature() { setOnClick { context.copyText(it.text) } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/RequestPathDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/RequestPathDialog.kt index f591db6..aac1521 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/RequestPathDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/RequestPathDialog.kt @@ -97,9 +97,11 @@ class RequestPathDialog : val currentInstance = observer.fileInstance if (currentInstance != null) { val context = requireContext() - if (currentInstance.path == "/" || currentInstance.path.startsWith(context.getCurrentUserEmulatedPath())) { + val userEmulatedPath = context.getCurrentUserEmulatedPath() + if (currentInstance.path == "/" || currentInstance.path.startsWith(userEmulatedPath)) { isEnabled = false - @Suppress("DEPRECATION") dialog?.onBackPressed() + @Suppress("DEPRECATION") + dialog?.onBackPressed() } else { scope.launch { observer.update( @@ -154,5 +156,4 @@ class RequestPathDialog : dismiss() } } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/SortDialogFragment.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/SortDialogFragment.kt index 4359874..e6eddab 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/SortDialogFragment.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/SortDialogFragment.kt @@ -44,9 +44,7 @@ class SortDialogFragment : CommonDialogFragment() { activeSortChains.value = activeList } - override fun onEditingChanged(editing: List>) { - - } + override fun onEditingChanged(editing: List>) = Unit } private val sortChains: List> @@ -68,4 +66,4 @@ class SortDialogFragment : CommonDialogFragment() { fun List.buildSorts() = map { NameSort(NameSort.Item()) - } \ No newline at end of file + } diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/TaskConfirmDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/TaskConfirmDialog.kt index 251d836..c63802e 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/TaskConfirmDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/TaskConfirmDialog.kt @@ -13,9 +13,12 @@ import kotlinx.parcelize.Parcelize import java.io.File class TaskConfirmDialog : SimpleDialogFragment(DialogTaskConfirmBinding::inflate) { - private val sharePasteTargetViewModel by keyPrefix({ "temp" }, pvm({}) { - SharePasteTargetViewModel() - }) + private val sharePasteTargetViewModel by keyPrefix( + { "temp" }, + pvm({}) { + SharePasteTargetViewModel() + } + ) override fun onBindViewEvent(binding: DialogTaskConfirmBinding) { binding.labelDest.text = sharePasteTargetViewModel.dest @@ -36,4 +39,4 @@ class TaskConfirmDialog : SimpleDialogFragment(DialogT @Parcelize class Result(val confirm: Boolean) : Parcelable -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/VolumeSpaceDialog.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/VolumeSpaceDialog.kt index 8a1fc01..be8a555 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/VolumeSpaceDialog.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/dialog/VolumeSpaceDialog.kt @@ -1,8 +1,11 @@ package com.storyteller_f.giant_explorer.dialog import android.os.Build +import android.os.storage.StorageVolume +import androidx.annotation.RequiresApi import androidx.core.view.isVisible import com.storyteller_f.common_ui.SimpleDialogFragment +import com.storyteller_f.common_ui.scope import com.storyteller_f.file_system.util.getFree import com.storyteller_f.file_system.util.getSpace import com.storyteller_f.file_system.util.getStorageCompat @@ -12,37 +15,58 @@ import com.storyteller_f.file_system.util.volumePathName import com.storyteller_f.giant_explorer.control.format1024 import com.storyteller_f.giant_explorer.databinding.DialogVolumeSpaceBinding import com.storyteller_f.giant_explorer.databinding.LayoutVolumeItemBinding +import kotlinx.coroutines.launch +import java.io.File class VolumeSpaceDialog : SimpleDialogFragment(DialogVolumeSpaceBinding::inflate) { override fun onBindViewEvent(binding: DialogVolumeSpaceBinding) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { requireContext().getStorageVolume().forEach { - LayoutVolumeItemBinding.inflate(layoutInflater, binding.spaceList, true).apply { - val prefix = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - it.directory?.absolutePath - } else { - volumePathName(it.uuid) - } - volumeSpace.text = format1024(getSpace(prefix)) - volumeFree.text = format1024(getFree(prefix)) - volumeTotal.text = format1024(getTotal(prefix)) - listOf(volumeSpace, volumeTotal, volumeFree).forEach { - it.copyTextFeature() - } - volumeName.text = it.getDescription(requireContext()) - info.isVisible = true - volumeState.text = it.state - } + deployStorage(binding, it) } } else { requireContext().getStorageCompat().forEach { - LayoutVolumeItemBinding.inflate(layoutInflater, binding.spaceList, true).apply { - volumeSpace.text = format1024(getSpace(it.absolutePath)) - volumeName.text = it.name - info.isVisible = false - } + deployFile(binding, it) } } } -} \ No newline at end of file + + private fun deployFile( + binding: DialogVolumeSpaceBinding, + it: File + ) { + LayoutVolumeItemBinding.inflate(layoutInflater, binding.spaceList, true).apply { + scope.launch { + volumeSpace.text = format1024(getSpace(it.absolutePath)) + } + volumeName.text = it.name + info.isVisible = false + } + } + + @RequiresApi(Build.VERSION_CODES.N) + private fun deployStorage( + binding: DialogVolumeSpaceBinding, + it: StorageVolume + ) { + LayoutVolumeItemBinding.inflate(layoutInflater, binding.spaceList, true).apply { + val prefix = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + it.directory?.absolutePath + } else { + volumePathName(it.uuid) + } + scope.launch { + volumeSpace.text = format1024(getSpace(prefix)) + volumeFree.text = format1024(getFree(prefix)) + volumeTotal.text = format1024(getTotal(prefix)) + } + listOf(volumeSpace, volumeTotal, volumeFree).forEach { + it.copyTextFeature() + } + volumeName.text = it.getDescription(requireContext()) + info.isVisible = true + volumeState.text = it.state + } + } +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameFilter.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameFilter.kt index 2bb992e..f00bc73 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameFilter.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameFilter.kt @@ -28,9 +28,7 @@ class NameFilter(item: SimpleRegExpConfigItem) : override fun dup(): Any { return Config(regexp) } - } - } class FilterFactory : FilterViewHolderFactory() { @@ -41,5 +39,4 @@ class FilterFactory : FilterViewHolderFactory() { SimpleRegExpFilterViewHolder.create(container) return NameFilter.ViewHolder(container.view) } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameSort.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameSort.kt index ddf057c..cc81aa5 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameSort.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/filter/NameSort.kt @@ -29,7 +29,6 @@ class NameSort(item: SortConfigItem) : SortChain("name sort", item) { override fun dup(): Any { return Item(sortDirection) } - } } @@ -41,5 +40,4 @@ class SortFactory : SortViewHolderFactory() { SortItemViewHolder.Simple.create(container) return NameSort.ViewHolder(container.view) } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/model/FileModel.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/model/FileModel.kt index fe0fc06..b164a91 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/model/FileModel.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/model/FileModel.kt @@ -19,4 +19,3 @@ data class FileModel( ) : Model { override fun commonId() = fullPath } - diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateBinder.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateBinder.kt index 06b4ac1..c9e09f3 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateBinder.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateBinder.kt @@ -2,7 +2,6 @@ package com.storyteller_f.giant_explorer.service import android.content.ContentResolver import android.content.Context -import android.net.Uri import android.os.Binder import android.util.Log import androidx.annotation.WorkerThread @@ -46,7 +45,10 @@ class FileOperateBinder(val context: Context) : Binder() { override fun onLeft(fileCount: Int, folderCount: Int, size: Long, key: String) { fileOperationProgressListener[key]?.forEach { it.onLeft( - fileCount, folderCount, size, key + fileCount, + folderCount, + size, + key ) } } @@ -54,7 +56,6 @@ class FileOperateBinder(val context: Context) : Binder() { override fun onComplete(dest: String?, isSuccess: Boolean, key: String) { fileOperationProgressListener[key]?.forEach { it.onComplete(dest, isSuccess, key) } } - } val state = MutableLiveData(state_null) var fileOperateResultContainer: WeakReference = WeakReference(null) @@ -106,7 +107,9 @@ class FileOperateBinder(val context: Context) : Binder() { @WorkerThread private suspend fun startDeleteTask( - focused: FileInfo, selected: List, key: String + focused: FileInfo, + selected: List, + key: String ) { state.postValue(state_computing) val assessResult = runBlocking { @@ -114,7 +117,11 @@ class FileOperateBinder(val context: Context) : Binder() { } state.postValue(state_running) val deleteForemanImpl = DeleteForemanImpl( - selected, context, assessResult.toOverview(), focused, key + selected, + context, + assessResult.toOverview(), + focused, + key ).attachListener() if (deleteForemanImpl.call()) { whenEnd(key) @@ -143,7 +150,13 @@ class FileOperateBinder(val context: Context) : Binder() { } whenRunning(key, assessResult) val copyForemanImpl = CopyForemanImpl( - selected, deleteOrigin, dest, context, assessResult.toOverview(), focused, key + selected, + deleteOrigin, + dest, + context, + assessResult.toOverview(), + focused, + key ).attachListener() if (copyForemanImpl.call()) { whenEnd(key) @@ -151,23 +164,6 @@ class FileOperateBinder(val context: Context) : Binder() { } } - private fun detectUri(selected: List, key: String): List? { - state.postValue(state_detect) - val supportTasks = selected.filter { - supportUri.contains(it.scheme) - } - if (supportTasks.isEmpty()) { - whenError(key, "没有合法任务") - return null - } - val errorTasks = selected - supportTasks.toSet() - if (errorTasks.isNotEmpty()) { - whenError(key, "unrecognized uri: ${errorTasks.joinToString()}") - return null - } - return supportTasks - } - private fun whenRunning(key: String, computeSize: TaskAssessResult) { Log.d(TAG, "whenRunning() called with: key = $key, computeSize = $computeSize") map[key] = TaskSession(computeSize, null) @@ -188,13 +184,6 @@ class FileOperateBinder(val context: Context) : Binder() { map.remove(key) } - private fun whenError(key: String, message: String) { - Log.d(TAG, "whenError() called with: key = $key, message = $message") - map[key] = TaskSession(null, message) - state.postValue(state_error) - fileOperateResultContainer.get()?.onError(message) - } - private fun FileOperationForeman.attachListener(): FileOperationForeman { fileOperationForemanProgressListener = progressListenerLocal return this @@ -275,11 +264,16 @@ class TaskAssessor( private var count = 0 private var folderCount = 0 suspend fun assess(): TaskAssessResult { + dest ?: return TaskAssessResult.empty val size = detectorTasks.map { - if (dest != null && FileOperateBinder.checkOperationValid( - it.fullPath, dest.path + require( + FileOperateBinder.checkOperationValid( + it.fullPath, + dest.path ) - ) throw Exception("不能将父文件夹移动到子文件夹") + ) { + "不能将父文件夹移动到子文件夹" + } val fileInstance = getFileInstance(context, File(it.fullPath).toUri()) if (!fileInstance.exists()) { throw FileNotFoundException(fileInstance.path) @@ -291,7 +285,9 @@ class TaskAssessor( getDirectorySize(it) } }.plus(0).reduce { acc, l -> acc + l } - if (count.toLong() + folderCount.toLong() + size == 0L) throw Exception("无合法任务") + require(count.toLong() + folderCount.toLong() + size > 0L) { + "无合法任务" + } return TaskAssessResult(count, folderCount, size) } @@ -309,5 +305,4 @@ class TaskAssessor( }.plus(0).reduce { acc, l -> acc + l } return fileSize + directorySize } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateService.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateService.kt index 2e4c718..33b7a2c 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateService.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileOperateService.kt @@ -55,4 +55,4 @@ class FileOperateService : Service() { companion object { private const val TAG = "FileOperateService" } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileService.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileService.kt index 1a4be90..67a3de8 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileService.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileService.kt @@ -10,4 +10,4 @@ class FileService : RootService() { override fun onBind(intent: Intent): IBinder { return FileSystemManager.getService() } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileSystemProvider.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileSystemProvider.kt index c119907..a3d6f96 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileSystemProvider.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/FileSystemProvider.kt @@ -16,7 +16,6 @@ import com.storyteller_f.giant_explorer.control.plugin.FileSystemProviderResolve import com.storyteller_f.plugin_core.FileSystemProviderConstant import kotlinx.coroutines.runBlocking - class FileSystemProvider : ContentProvider() { override fun onCreate() = true @@ -90,9 +89,11 @@ class FileSystemProvider : ContentProvider() { override fun getType(uri: Uri): String? { val current = current(uri) ?: return null return runBlocking { - if (current.fileKind().isDirectory) DocumentsContract.Document.MIME_TYPE_DIR - else + if (current.fileKind().isDirectory) { + DocumentsContract.Document.MIME_TYPE_DIR + } else { singleton.getMimeTypeFromExtension(current.getFileInfo().extension) + } } } @@ -104,7 +105,6 @@ class FileSystemProvider : ContentProvider() { TODO("Not yet implemented") } - override fun update( uri: Uri, values: ContentValues?, @@ -124,4 +124,4 @@ class FileSystemProvider : ContentProvider() { DocumentsContract.Document.COLUMN_MIME_TYPE, ) } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/LocalFileOperationForeman.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/LocalFileOperationForeman.kt index 8346692..058d11c 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/LocalFileOperationForeman.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/service/LocalFileOperationForeman.kt @@ -29,7 +29,7 @@ abstract class FileOperationForeman( private var leftFileCount = overview.fileCount private var leftFolderCount = overview.folderCount var leftSize = overview.size - fun emitCurrentStateMessage() { + private fun emitCurrentStateMessage() { fileOperationForemanProgressListener?.onLeft(leftFileCount, leftFolderCount, leftSize, key) fileOperationForemanProgressListener?.onProgress(progress, key) } @@ -46,10 +46,9 @@ abstract class FileOperationForeman( get() { val sumCount = overview.sumCount val completedCount = sumCount - leftFolderCount - leftFileCount - return (completedCount * 1.0 / sumCount * 100).toInt() + return (completedCount * 1.0 / sumCount * PERCENT_BASE).toInt() } - override fun onFileDone(fileInstance: FileInstance?, message: Message?, size: Long) { leftFileCount-- leftSize -= size @@ -70,10 +69,16 @@ abstract class FileOperationForeman( key ) } + companion object { + const val PERCENT_BASE = 100 + } } abstract class LocalFileOperationForeman( - val focused: FileInfo?, context: Context, overview: TaskOverview, key: String + val focused: FileInfo?, + context: Context, + overview: TaskOverview, + key: String ) : FileOperationForeman(context, overview, key) { abstract val description: String? } @@ -114,13 +119,15 @@ class CopyForemanImpl( else -> ScopeFileMoveOp(fileInstance, target, context) }.call() - !operationResult//如果失败了,提前结束 + !operationResult // 如果失败了,提前结束 } if (!isSuccess) { emitDetailMessage("error in copy impl", Log.ERROR) } fileOperationForemanProgressListener?.onComplete( - target.path, isSuccess, key + target.path, + isSuccess, + key ) return isSuccess } @@ -128,9 +135,8 @@ class CopyForemanImpl( override val progress: Int get() { val doneSize: Long = overview.size - leftSize - return (doneSize * 100.0 / overview.size).toInt() + return (doneSize * PERCENT_BASE / overview.size).toInt() } - } class DeleteForemanImpl( @@ -152,7 +158,7 @@ class DeleteForemanImpl( } override suspend fun call(): Boolean { - val isSuccess = !detectorTasks.any {//如果有一个失败了,就提前退出 + val isSuccess = !detectorTasks.any { // 如果有一个失败了,就提前退出 emitStateMessage("处理${it.fullPath}") !FileDeleteOp( getFileInstance(context, File(it.fullPath).toUri()), @@ -164,5 +170,4 @@ class DeleteForemanImpl( fileOperationForemanProgressListener?.onComplete(focused?.fullPath, isSuccess, key) return isSuccess } - -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedDictionary.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedDictionary.kt index e36e6e8..a87eb80 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedDictionary.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedDictionary.kt @@ -49,4 +49,3 @@ class BEncodedDictionary internal constructor() { } } } - diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedList.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedList.kt index 2047c42..46762cc 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedList.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/BEncodedList.kt @@ -19,4 +19,4 @@ class BEncodedList : ArrayList() { buffer.append('e') return buffer.toString() } -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/Decode.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/Decode.kt index b21c8f6..eda877f 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/Decode.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/Decode.kt @@ -23,7 +23,6 @@ object Decode { return Pair(total, input.substring(splitIndex + 1, total)) } - /** * @return 返回消耗的字符个数 */ @@ -62,7 +61,7 @@ object Decode { } else -> { - throw Exception() + error("unrecognized $firstChar") } } @@ -78,7 +77,6 @@ object Decode { list.add(v) it.substring(l) } - } return (string.length - rest.length + 1) to list @@ -114,7 +112,6 @@ object Decode { ).buffered().readText() return bDecode(read) } - } private inline fun loop(init: T, block: (T) -> T?): T { @@ -124,4 +121,4 @@ private inline fun loop(init: T, block: (T) -> T?): T { temp = block1 } return temp -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/TorrentFile.kt b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/TorrentFile.kt index cdf65f9..9d486d9 100644 --- a/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/TorrentFile.kt +++ b/app/GiantExplorer/giant-explorer/src/main/java/com/storyteller_f/giant_explorer/utils/TorrentFile.kt @@ -25,4 +25,4 @@ suspend fun getTorrentName(inputStream: InputStream): String { encodedDictionary.get("name").toString().toByteArray(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8 ) -} \ No newline at end of file +} diff --git a/app/GiantExplorer/giant-explorer/src/main/res/layout/fragment_remote_detail.xml b/app/GiantExplorer/giant-explorer/src/main/res/layout/fragment_remote_detail.xml index 1a7fecd..d359294 100644 --- a/app/GiantExplorer/giant-explorer/src/main/res/layout/fragment_remote_detail.xml +++ b/app/GiantExplorer/giant-explorer/src/main/res/layout/fragment_remote_detail.xml @@ -97,10 +97,10 @@ android:layout_height="wrap_content" android:text="测试连接" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/button_second" /> + app:layout_constraintTop_toTopOf="@id/button_ok" />