Init
This commit is contained in:
commit
3242c9f4a0
139 changed files with 5879 additions and 0 deletions
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
3
.idea/.gitignore
vendored
Normal file
3
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
1
.idea/.name
Normal file
1
.idea/.name
Normal file
|
@ -0,0 +1 @@
|
|||
My Application
|
6
.idea/compiler.xml
Normal file
6
.idea/compiler.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
18
.idea/deploymentTargetSelector.xml
Normal file
18
.idea/deploymentTargetSelector.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2024-10-09T05:47:14.530453572Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/kagura/.android/avd/Pixel_6_API_33.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
19
.idea/gradle.xml
Normal file
19
.idea/gradle.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
53
.idea/inspectionProfiles/Project_Default.xml
Normal file
53
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
6
.idea/kotlinc.xml
Normal file
6
.idea/kotlinc.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.0.20" />
|
||||
</component>
|
||||
</project>
|
10
.idea/migrations.xml
Normal file
10
.idea/migrations.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
10
.idea/misc.xml
Normal file
10
.idea/misc.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
82
app/build.gradle.kts
Normal file
82
app/build.gradle.kts
Normal file
|
@ -0,0 +1,82 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.myapplication"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.myapplication"
|
||||
minSdk = 29
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
compose = true
|
||||
buildConfig = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.activity)
|
||||
implementation(libs.constraintlayout)
|
||||
implementation(libs.core.ktx)
|
||||
implementation(libs.navigation.fragment)
|
||||
implementation(libs.navigation.ui)
|
||||
implementation(libs.lifecycle.runtime.ktx)
|
||||
implementation(libs.activity.compose)
|
||||
implementation(platform(libs.compose.bom))
|
||||
implementation(libs.ui)
|
||||
implementation(libs.ui.graphics)
|
||||
implementation(libs.ui.tooling.preview)
|
||||
implementation(libs.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
androidTestImplementation(platform(libs.compose.bom))
|
||||
androidTestImplementation(libs.ui.test.junit4)
|
||||
debugImplementation(libs.ui.tooling)
|
||||
debugImplementation(libs.ui.test.manifest)
|
||||
|
||||
|
||||
implementation(libs.commons.io)
|
||||
}
|
21
app/proguard-rules.pro
vendored
Normal file
21
app/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,26 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.example.myapplication", appContext.getPackageName());
|
||||
}
|
||||
}
|
78
app/src/main/AndroidManifest.xml
Normal file
78
app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@drawable/ic_launcher_foreground"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@drawable/ic_launcher_foreground"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".compose.RequirePermissionActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.MyApplication">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".compose.ViewFileActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".store_page"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".document_page_search"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".document_page"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".music_page_search"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".music_page"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".video_page_search"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".picture_page_search"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".main_page"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".picture_page"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".video_page"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.example.myapplication.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
BIN
app/src/main/ic_camera-playstore.png
Normal file
BIN
app/src/main/ic_camera-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
app/src/main/ic_picture1-playstore.png
Normal file
BIN
app/src/main/ic_picture1-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
BIN
app/src/main/ic_wechat-playstore.png
Normal file
BIN
app/src/main/ic_wechat-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,33 @@
|
|||
package com.example.myapplication.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.example.myapplication.R
|
||||
import java.io.File
|
||||
|
||||
class DocumentModel(document: File) {
|
||||
val name: String = document.name
|
||||
|
||||
init {
|
||||
if (!document.isFile) {
|
||||
throw RuntimeException("No such document")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentAdapter(context: Context, list: ArrayList<DocumentModel>) :
|
||||
ArrayAdapter<DocumentModel>(context, 0, list) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val listView = convertView ?: LayoutInflater.from(context).inflate(
|
||||
R.layout.document_card_item, parent, false
|
||||
)
|
||||
val model = getItem(position) ?: throw RuntimeException()
|
||||
val card = listView.findViewById<TextView>(R.id.iconButton)
|
||||
card.text = model.name
|
||||
return listView
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.example.myapplication.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.media.ThumbnailUtils
|
||||
import android.util.Size
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AbsListView.LayoutParams
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.GridView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.example.myapplication.R
|
||||
import java.io.File
|
||||
|
||||
class ImageModel(image: File) {
|
||||
val name: String = image.name
|
||||
var thumbnail: Bitmap
|
||||
|
||||
init {
|
||||
if (!image.isFile) {
|
||||
throw RuntimeException("No such Image")
|
||||
}
|
||||
thumbnail = ThumbnailUtils.createImageThumbnail(image, Size(512, 512), null)
|
||||
}
|
||||
}
|
||||
|
||||
class ImageAdapter(context: Context, list: ArrayList<ImageModel>) :
|
||||
ArrayAdapter<ImageModel>(context, 0, list) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val listView = convertView ?: LayoutInflater.from(context).inflate(
|
||||
R.layout.picture_card_item, parent, false
|
||||
)
|
||||
val model = getItem(position) ?: throw RuntimeException()
|
||||
listView.findViewById<ImageView>(R.id.pictureCardImage).setImageBitmap(model.thumbnail)
|
||||
listView.findViewById<TextView>(R.id.pictureCardText).text = model.name
|
||||
listView.setLayoutParams(LayoutParams(GridView.AUTO_FIT, 530))
|
||||
|
||||
return listView
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.example.myapplication.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.example.myapplication.R
|
||||
import java.io.File
|
||||
|
||||
class MusicModel(music: File) {
|
||||
val name: String = music.nameWithoutExtension // 歌曲就不放扩展名了
|
||||
|
||||
init {
|
||||
if (!music.isFile) {
|
||||
throw RuntimeException("No such Video")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MusicAdapter(context: Context, list: ArrayList<MusicModel>) :
|
||||
ArrayAdapter<MusicModel>(context, 0, list) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val listView = convertView ?: LayoutInflater.from(context).inflate(
|
||||
R.layout.music_card_item, parent, false
|
||||
)
|
||||
val model = getItem(position) ?: throw RuntimeException()
|
||||
val card = listView.findViewById<TextView>(R.id.iconButton)
|
||||
card.text = model.name
|
||||
return listView
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.example.myapplication.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.media.ThumbnailUtils
|
||||
import android.util.Size
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AbsListView.LayoutParams
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.GridView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.example.myapplication.R
|
||||
import java.io.File
|
||||
|
||||
class VideoModel(video: File) {
|
||||
val name: String = video.name
|
||||
var thumbnail: Bitmap
|
||||
|
||||
init {
|
||||
if (!video.isFile) {
|
||||
throw RuntimeException("No such Video")
|
||||
}
|
||||
thumbnail = ThumbnailUtils.createVideoThumbnail(
|
||||
video, Size(854, 480) // 考虑到视频多16:9
|
||||
, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class VideoAdapter(context: Context, list: ArrayList<VideoModel>) :
|
||||
ArrayAdapter<VideoModel>(context, 0, list) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val listView = convertView ?: LayoutInflater.from(context).inflate(
|
||||
R.layout.picture_card_item, parent, false
|
||||
)
|
||||
val model = getItem(position) ?: throw RuntimeException()
|
||||
listView.findViewById<ImageView>(R.id.pictureCardImage).setImageBitmap(model.thumbnail)
|
||||
listView.findViewById<TextView>(R.id.pictureCardText).text = model.name
|
||||
listView.setLayoutParams(LayoutParams(GridView.AUTO_FIT, 530))
|
||||
|
||||
return listView
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.example.myapplication.compose
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
class PasteHelper {
|
||||
companion object{
|
||||
fun copyDirectory(sourceDir: File, destDir: File) {
|
||||
// creates the destination directory if it does not exist
|
||||
if (!destDir.exists()) {
|
||||
destDir.mkdirs()
|
||||
}
|
||||
|
||||
// throws exception if the source does not exist
|
||||
require(sourceDir.exists()) { "sourceDir does not exist" }
|
||||
|
||||
// throws exception if the arguments are not directories
|
||||
require(!(sourceDir.isFile || destDir.isFile)) { "Either sourceDir or destDir is not a directory" }
|
||||
|
||||
copyDirectoryImpl(sourceDir, destDir)
|
||||
}
|
||||
|
||||
private fun copyDirectoryImpl(sourceDir: File, destDir: File) {
|
||||
val items = sourceDir.listFiles()
|
||||
if (items != null && items.isNotEmpty()) {
|
||||
for (anItem: File in items) {
|
||||
if (anItem.isDirectory) {
|
||||
val newDir = File(destDir, anItem.name)
|
||||
newDir.mkdir()
|
||||
// copy the directory (recursive call)
|
||||
copyDirectory(anItem, newDir)
|
||||
} else {
|
||||
// copy the file
|
||||
val destFile = File(destDir, anItem.name)
|
||||
copySingleFile(anItem, destFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copySingleFile(sourceFile: File, destFile: File){
|
||||
if (!destFile.exists()) {
|
||||
destFile.createNewFile()
|
||||
}
|
||||
var sourceChannel: FileChannel? = null
|
||||
var destChannel: FileChannel? = null
|
||||
|
||||
try {
|
||||
sourceChannel = FileInputStream(sourceFile).channel
|
||||
destChannel = FileOutputStream(destFile).channel
|
||||
sourceChannel.transferTo(0, sourceChannel.size(), destChannel)
|
||||
} finally {
|
||||
sourceChannel?.close()
|
||||
destChannel?.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package com.example.myapplication.compose
|
||||
|
||||
import android.Manifest.permission
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.example.myapplication.BuildConfig
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.VideoLister
|
||||
import com.example.myapplication.main_page
|
||||
|
||||
class RequirePermissionActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
val activity = this
|
||||
window.statusBarColor = getColor(R.color.WhiteSmoke)
|
||||
setContent {
|
||||
Column(
|
||||
modifier = Modifier.statusBarsPadding()
|
||||
.fillMaxHeight(0.9f)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = getString(
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.R) {
|
||||
R.string.require_manage_storage
|
||||
} else {
|
||||
R.string.require_permission_readwrite
|
||||
}
|
||||
),
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
fontSize = 30.sp
|
||||
)
|
||||
Button(
|
||||
onClick = { // Ask for permission
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||
val uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
|
||||
intent.setData(uri)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
}
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
|
||||
val perm33 = arrayOf(permission.READ_MEDIA_AUDIO,permission.READ_MEDIA_VIDEO,permission.READ_MEDIA_IMAGES)
|
||||
ActivityCompat.requestPermissions(
|
||||
activity, perm33, 101
|
||||
)
|
||||
}
|
||||
} else { // for legacy system
|
||||
val permissions =
|
||||
arrayOf(permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE)
|
||||
ActivityCompat.requestPermissions(
|
||||
activity, permissions, 100
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = ButtonColors(
|
||||
containerColor = Color(0xFF039BE5),
|
||||
contentColor = Color.White,
|
||||
disabledContainerColor = Color.Gray,
|
||||
disabledContentColor = Color.White
|
||||
)
|
||||
) {
|
||||
Text(text = getString(R.string.give_permission))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (checkPermissions(this)) {
|
||||
val intent = Intent(this, main_page::class.java)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
initSystem()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkPermissions(context: Context): Boolean {
|
||||
// Check storage permission
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.R) {
|
||||
// Check manage storage on R+
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
return false
|
||||
}
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
|
||||
val perm33 = arrayOf(permission.READ_MEDIA_AUDIO,permission.READ_MEDIA_VIDEO,permission.READ_MEDIA_IMAGES)
|
||||
perm33.forEach {
|
||||
if (context.checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val permissions = arrayOf(permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE)
|
||||
permissions.forEach {
|
||||
if (context.checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun initSystem(){
|
||||
ImageLister.instance.initialize()
|
||||
VideoLister.instance.initialize()
|
||||
MusicLister.instance.initialize()
|
||||
DocumentLister.instance.initialize()
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.example.myapplication.compose
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.compose.ui.FileColumn
|
||||
import java.io.File
|
||||
|
||||
class ViewFileActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
var path =
|
||||
intent.extras?.getString("folder") ?: Environment.getExternalStorageDirectory().path
|
||||
val file = File(path)
|
||||
if (!file.isDirectory){
|
||||
path = Environment.getExternalStorageDirectory().path
|
||||
}
|
||||
enableEdgeToEdge()
|
||||
window.statusBarColor = getColor(R.color.WhiteSmoke)
|
||||
setContent {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(getColor(R.color.WhiteSmoke)))
|
||||
){
|
||||
FileColumn(this).Draw(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,693 @@
|
|||
package com.example.myapplication.compose.ui
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.draganddrop.dragAndDropSource
|
||||
import androidx.compose.foundation.draganddrop.dragAndDropTarget
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draganddrop.DragAndDropEvent
|
||||
import androidx.compose.ui.draganddrop.DragAndDropTarget
|
||||
import androidx.compose.ui.draganddrop.DragAndDropTransferData
|
||||
import androidx.compose.ui.draganddrop.mimeTypes
|
||||
import androidx.compose.ui.draganddrop.toAndroidDragEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.FileProvider
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.compose.PasteHelper
|
||||
import com.example.myapplication.fileSystem.CutHelper
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import com.example.myapplication.fileSystem.WrappedFile.Type
|
||||
import com.example.myapplication.main_page
|
||||
import com.example.myapplication.utils.AlertHelper
|
||||
import com.example.myapplication.utils.ClipHelper
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class FileColumn(val context: Context) {
|
||||
private val fileList = mutableStateListOf<WrappedFile>()
|
||||
|
||||
@Composable
|
||||
fun Draw(startFolder: String) {
|
||||
var path by remember { mutableStateOf(startFolder) }
|
||||
var shouldUpdate by remember { mutableStateOf(false) }
|
||||
val cwd = File(path)
|
||||
if (!cwd.isDirectory) {
|
||||
return
|
||||
}
|
||||
var isOkay by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(path, shouldUpdate) {
|
||||
isOkay = false
|
||||
fileList.clear()
|
||||
cwd.listFiles()?.forEach { f ->
|
||||
fileList.add(WrappedFile(f))
|
||||
}
|
||||
isOkay = true
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.statusBarsPadding()
|
||||
.navigationBarsPadding()
|
||||
.background(Color(context.getColor(R.color.WhiteSmoke)))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
val intent = Intent(
|
||||
context,
|
||||
main_page::class.java
|
||||
)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_left_arrow), "back"
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = path,
|
||||
fontSize = 24.sp,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
}
|
||||
if (!isOkay) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = context.getString(R.string.loading),
|
||||
fontSize = 34.sp
|
||||
)
|
||||
}
|
||||
} else {
|
||||
DrawColumns(fileList, path, update = { shouldUpdate = !shouldUpdate }) {
|
||||
if (it == "/storage/emulated") {
|
||||
return@DrawColumns
|
||||
}
|
||||
val file = File(it)
|
||||
if (file.isDirectory) {
|
||||
path = it
|
||||
} else if (file.isFile) {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
context.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, WrappedFile(file).mime)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object{
|
||||
var dropTarget: String? = null
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun DrawColumns(
|
||||
fileList: List<WrappedFile>,
|
||||
cwd: String,
|
||||
parent: String? = null,
|
||||
update: (() -> Unit)? = null,
|
||||
onItemClick: ((String) -> Unit)? = null
|
||||
) {
|
||||
val dragAndDropCallBack = remember {
|
||||
object : DragAndDropTarget {
|
||||
override fun onDrop(event: DragAndDropEvent): Boolean {
|
||||
val target = File(dropTarget ?: return false)
|
||||
if (!target.exists()){
|
||||
return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
}
|
||||
val data = event.toAndroidDragEvent()
|
||||
.clipData.getItemAt(0).text
|
||||
if (!data.startsWith("Drag:")) {
|
||||
return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
}
|
||||
|
||||
val source = File(data.split(':').last())
|
||||
if (!source.exists()) {
|
||||
return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
}
|
||||
|
||||
if (source.path == target.path){
|
||||
dropTarget = null
|
||||
return false
|
||||
}
|
||||
|
||||
if (target.isFile) {
|
||||
if (source.isFile) {
|
||||
val dir = source.parent ?: return false
|
||||
val f = File("$dir/合并文件夹")
|
||||
if (!f.exists()){
|
||||
f.mkdir()
|
||||
}
|
||||
ClipHelper.getInstance(context).copy(source,context)
|
||||
val sourceUri = ClipHelper.getInstance(context).paste() ?: return false
|
||||
val inputStream = try {
|
||||
context.contentResolver.openInputStream(sourceUri)
|
||||
}catch (e: FileNotFoundException) {
|
||||
return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
}
|
||||
if (inputStream != null) {
|
||||
val actualFile = File(f, source.name)
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
}
|
||||
ClipHelper.getInstance(context).copy(target,context)
|
||||
val targetUri = ClipHelper.getInstance(context).paste() ?: return false
|
||||
val inputStream2 = context.contentResolver.openInputStream(targetUri)
|
||||
if (inputStream2 != null) {
|
||||
val actualFile = File(f, target.name)
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream2))
|
||||
inputStream2.close()
|
||||
}
|
||||
Toast.makeText(
|
||||
context,
|
||||
"已放入 $dir/合并文件夹",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else if (source.isDirectory) {
|
||||
ClipHelper.getInstance(context).copy(target,context)
|
||||
val sourceUri = ClipHelper.getInstance(context).paste() ?: return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
val inputStream = try {
|
||||
context.contentResolver.openInputStream(sourceUri)
|
||||
}catch (e: FileNotFoundException) {
|
||||
return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
}
|
||||
if (inputStream != null) {
|
||||
val actualFile = File(source, target.name)
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
}
|
||||
Toast.makeText(
|
||||
context,
|
||||
"已放入 ${source.path}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else if (target.isDirectory) {
|
||||
if (source.isFile) {
|
||||
ClipHelper.getInstance(context).copy(source,context)
|
||||
val sourceUri = ClipHelper.getInstance(context).paste() ?: return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
val inputStream = try {
|
||||
context.contentResolver.openInputStream(sourceUri)
|
||||
}catch (e: FileNotFoundException) {
|
||||
return false.also {
|
||||
dropTarget = null
|
||||
}
|
||||
}
|
||||
if (inputStream != null) {
|
||||
val actualFile = File(target, source.name)
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
}
|
||||
Toast.makeText(
|
||||
context,
|
||||
"已放入 ${target.path}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
update?.invoke()
|
||||
dropTarget = null
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(vertical = 5.dp)
|
||||
) {
|
||||
// 最顶上那个
|
||||
if (parent == null) {
|
||||
val parts = cwd.split('/')
|
||||
if (parts.lastIndex != 0) {
|
||||
val prevDir = StringBuilder()
|
||||
for (i in 0..<parts.lastIndex) {
|
||||
prevDir.append("/${parts[i]}")
|
||||
}
|
||||
val prev = File(prevDir.toString())
|
||||
if (prev.isDirectory) {
|
||||
item {
|
||||
FileSingleView(
|
||||
WrappedFile(prev),
|
||||
cwd,
|
||||
ImageVector.vectorResource(R.drawable.outline_arrow_upward_32),
|
||||
context.getString(R.string.prev_folder),
|
||||
prev.path,
|
||||
update
|
||||
) {
|
||||
onItemClick?.invoke(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
FileSingleView(
|
||||
WrappedFile(File(parent)),
|
||||
cwd,
|
||||
ImageVector.vectorResource(R.drawable.outline_arrow_upward_32),
|
||||
context.getString(R.string.prev_folder),
|
||||
parent,
|
||||
update
|
||||
) {
|
||||
onItemClick?.invoke(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 下面的内容
|
||||
items(fileList) { file ->
|
||||
FileSingleView(
|
||||
file, cwd,
|
||||
update = update,
|
||||
dragAndDrop = Modifier
|
||||
.dragAndDropSource {
|
||||
detectTapGestures(onLongPress = {
|
||||
startTransfer(
|
||||
DragAndDropTransferData(
|
||||
ClipData.newPlainText(
|
||||
"Drag", "Drag:${file.path}"
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
,
|
||||
dragAndDropCallBack = dragAndDropCallBack,
|
||||
onItemClick = onItemClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun FileSingleView(
|
||||
file: WrappedFile,
|
||||
cwd: String,
|
||||
forceIcon: ImageVector? = null,
|
||||
forceName: String? = null,
|
||||
forceParent: String? = null,
|
||||
update: (() -> Unit)? = null,
|
||||
dragAndDrop: Modifier = Modifier,
|
||||
dragAndDropCallBack: DragAndDropTarget? = null,
|
||||
onItemClick: ((String) -> Unit)? = null
|
||||
) {
|
||||
val openFileDialog = remember { mutableIntStateOf(3) }
|
||||
when (openFileDialog.intValue) {
|
||||
0 -> AskForName(
|
||||
onDismissRequest = { openFileDialog.intValue = 3 },
|
||||
onConfirmation = {
|
||||
if (it.isEmpty()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_need_input_name),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
val f = File("$cwd/$it")
|
||||
if (f.exists()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_already_exist),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
f.mkdir()
|
||||
}
|
||||
}
|
||||
update?.invoke()
|
||||
openFileDialog.intValue = 3
|
||||
},
|
||||
dir = cwd,
|
||||
isDirectory = true
|
||||
)
|
||||
|
||||
1 -> AskForName(
|
||||
onDismissRequest = { openFileDialog.intValue = 3 },
|
||||
onConfirmation = {
|
||||
if (it.isEmpty()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_need_input_name),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
val f = File("$cwd/$it")
|
||||
if (f.exists()) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_already_exist),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
f.createNewFile()
|
||||
}
|
||||
}
|
||||
update?.invoke()
|
||||
openFileDialog.intValue = 3
|
||||
},
|
||||
dir = cwd,
|
||||
isDirectory = false
|
||||
)
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = Dp.Hairline,
|
||||
color = Color.Gray,
|
||||
shape = RectangleShape
|
||||
)
|
||||
.padding(vertical = 3.dp)
|
||||
.clickable {
|
||||
onItemClick?.invoke(file.path)
|
||||
}.then(
|
||||
if (dragAndDropCallBack != null) {
|
||||
Modifier.dragAndDropTarget(
|
||||
shouldStartDragAndDrop = { event ->
|
||||
val result = event
|
||||
.mimeTypes()
|
||||
.contains(ClipDescription.MIMETYPE_TEXT_PLAIN)
|
||||
if (result) {
|
||||
dropTarget = file.path
|
||||
}
|
||||
result
|
||||
}, target = dragAndDropCallBack
|
||||
)
|
||||
}else{
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
|
||||
,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
forceIcon ?: ImageVector.vectorResource(
|
||||
when (file.mime.split('/').first()) {
|
||||
"dir" -> R.drawable.type_directory
|
||||
"image" -> R.drawable.type_image
|
||||
"video" -> R.drawable.type_video
|
||||
"audio" -> R.drawable.type_audio
|
||||
else -> R.drawable.type_file
|
||||
}
|
||||
), file.mime,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.then(dragAndDrop)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 15.dp)
|
||||
.fillMaxWidth(0.8f)
|
||||
) {
|
||||
Text(
|
||||
text = forceName ?: file.name,
|
||||
fontSize = 24.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = forceParent ?: file.getModifiedTimeString(context),
|
||||
fontSize = 15.sp,
|
||||
color = Color.Gray,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
if (forceName == null) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (file.type == Type.FILE) { // 普通文件
|
||||
AlertHelper.showNoPasteAlert(context,
|
||||
onCopy = {
|
||||
val f = File(file.path)
|
||||
if (f.isFile) {
|
||||
ClipHelper.getInstance(context).copy(f, context)
|
||||
}
|
||||
},
|
||||
onPaste = {
|
||||
|
||||
},
|
||||
onDelete = {
|
||||
AlertHelper.showDeleteAlert(context, file.path) {
|
||||
update?.invoke()
|
||||
}
|
||||
},
|
||||
onCut = {
|
||||
CutHelper.cut(context, File(file.path))
|
||||
update?.invoke()
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(context, file.path)
|
||||
}
|
||||
)
|
||||
} else { // 普通文件夹
|
||||
AlertHelper.showNoPasteAlert(context,
|
||||
onCopy = {
|
||||
val f = File(file.path)
|
||||
if (f.isDirectory) {
|
||||
ClipHelper.getInstance(context).copyFolder(f.path)
|
||||
}
|
||||
},
|
||||
onPaste = {
|
||||
|
||||
},
|
||||
onDelete = {
|
||||
AlertHelper.showDeleteAlert(context, file.path) {
|
||||
update?.invoke()
|
||||
}
|
||||
},
|
||||
onCut = {
|
||||
val f = File(file.path)
|
||||
if (f.isDirectory) {
|
||||
CutHelper.cutFolder(context, f)
|
||||
}
|
||||
update?.invoke()
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(context, file.path)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 10.dp)
|
||||
) {
|
||||
Image(
|
||||
ImageVector.vectorResource(R.drawable.outline_info_24), "info"
|
||||
)
|
||||
}
|
||||
} else { // 最上面那个按钮
|
||||
IconButton(
|
||||
onClick = {
|
||||
AlertHelper.showOnlyPasteInfoNewAlert(context,
|
||||
onPaste = {
|
||||
val uri = ClipHelper.getInstance(context).paste()
|
||||
if (uri != null) {
|
||||
val name = uri.path?.split('/')?.last() ?: "somePastedItem"
|
||||
val ext = name.split('.').last()
|
||||
var actualFile = File(cwd, name)
|
||||
while (actualFile.exists()) {
|
||||
actualFile = File("${actualFile.path}_paste.$ext")
|
||||
}
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
if (inputStream != null) {
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
// 刷新
|
||||
update?.invoke()
|
||||
}
|
||||
} else {
|
||||
val pasteDir = ClipHelper.getInstance(context).pasteFolder()
|
||||
if (pasteDir != null) {
|
||||
val sourceDir = File(pasteDir)
|
||||
if (sourceDir.isDirectory) {
|
||||
var destDir = File("$cwd/${sourceDir.name}")
|
||||
while (destDir.exists()) {
|
||||
destDir = File("${destDir.path}_paste")
|
||||
}
|
||||
destDir.mkdir()
|
||||
PasteHelper.copyDirectory(sourceDir, destDir)
|
||||
}
|
||||
update?.invoke()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_nothing_to_paste),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(context, cwd)
|
||||
},
|
||||
onNewFile = {
|
||||
openFileDialog.intValue = 1
|
||||
},
|
||||
onNewFolder = {
|
||||
openFileDialog.intValue = 0
|
||||
}
|
||||
)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 10.dp)
|
||||
) {
|
||||
Image(
|
||||
ImageVector.vectorResource(R.drawable.outline_info_i_24), "info"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AskForName(
|
||||
onDismissRequest: () -> Unit,
|
||||
onConfirmation: (String) -> Unit,
|
||||
dir: String,
|
||||
isDirectory: Boolean
|
||||
) {
|
||||
var input by remember { mutableStateOf("") }
|
||||
AlertDialog(
|
||||
icon = {
|
||||
Icon(
|
||||
ImageVector.vectorResource(R.drawable.baseline_question_mark_24),
|
||||
contentDescription = "Ask"
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(text = context.getString(R.string.input_name))
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = context.getString(
|
||||
if (isDirectory) {
|
||||
R.string.create_directory
|
||||
} else {
|
||||
R.string.create_file
|
||||
}, dir
|
||||
),
|
||||
modifier = Modifier.padding(vertical = 5.dp)
|
||||
)
|
||||
|
||||
TextField(
|
||||
value = input,
|
||||
onValueChange = { input = it },
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
onDismissRequest = {
|
||||
onDismissRequest()
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onConfirmation(input)
|
||||
}
|
||||
) {
|
||||
Text(context.getString(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
}
|
||||
) {
|
||||
Text(context.getString(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
284
app/src/main/java/com/example/myapplication/document_page.kt
Normal file
284
app/src/main/java/com/example/myapplication/document_page.kt
Normal file
|
@ -0,0 +1,284 @@
|
|||
package com.example.myapplication
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.GridView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AlertDialog.Builder
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type
|
||||
import com.example.myapplication.adapters.DocumentAdapter
|
||||
import com.example.myapplication.adapters.DocumentModel
|
||||
import com.example.myapplication.fileSystem.CutHelper
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister.Companion.instance
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister.Companion.regex
|
||||
import com.example.myapplication.utils.AlertHelper
|
||||
import com.example.myapplication.utils.ClipHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
|
||||
class document_page : AppCompatActivity() {
|
||||
private var documentList = listOf<String>()
|
||||
private val pasteDir = "${Environment.getExternalStorageDirectory().path}/Documents/pasted"
|
||||
private var listOrderType = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
setContentView(R.layout.document_page)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById(R.id.main)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
val systemBars =
|
||||
insets.getInsets(Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
// 设置排序按钮的点击事件
|
||||
val sortImageView = findViewById<ImageView>(R.id.sortDocumentView)
|
||||
sortImageView.setOnClickListener { v: View? -> showSortOptions() }
|
||||
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
val leftArrowImageView = findViewById<ImageView>(R.id.leftArrowImageView)
|
||||
leftArrowImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@document_page,
|
||||
main_page::class.java
|
||||
)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
val searchImageView = findViewById<ImageView>(R.id.searchDocumentView)
|
||||
searchImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@document_page,
|
||||
document_page_search::class.java
|
||||
)
|
||||
startActivity(intent) // 跳转到搜索页面
|
||||
}
|
||||
|
||||
findViewById<ImageView>(R.id.refreshData).setOnClickListener { _ ->
|
||||
update()
|
||||
}
|
||||
|
||||
val documentGrid: GridView = findViewById(R.id.DocumentGrid)
|
||||
|
||||
documentGrid.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
val file = File(documentList[position])
|
||||
if (file.isFile) {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
this,
|
||||
applicationContext.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, when(file.extension){
|
||||
"xls" -> "application/vnd.ms-excel"
|
||||
"xlsx" -> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
"doc" -> "application/msword"
|
||||
"docx" -> "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
"ppt" -> "application/vnd.ms-powerpoint"
|
||||
"pptx" -> "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||
"txt" -> "text/plain"
|
||||
"htm","html" -> "text/html"
|
||||
"pdf" -> "application/pdf"
|
||||
|
||||
else -> "application/*"
|
||||
})
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
try { startActivity(intent) }
|
||||
catch (e: ActivityNotFoundException){
|
||||
Toast.makeText(baseContext,"没有安装可以打开此类型文件的应用",Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
documentGrid.onItemLongClickListener =
|
||||
AdapterView.OnItemLongClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
showDocumentOperation(
|
||||
position
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
// 最后在后台刷新
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
val defaultText = loadingTextView.text
|
||||
launch { loadingText(loadingTextView,defaultText) }
|
||||
documentList = instance.dateOrderedList()
|
||||
val models = ArrayList<DocumentModel>()
|
||||
for (path in documentList) {
|
||||
models.add(DocumentModel(File(path)))
|
||||
}
|
||||
runOnUiThread {
|
||||
val adapter = DocumentAdapter(this@document_page, models)
|
||||
val grid = findViewById<GridView>(R.id.DocumentGrid)
|
||||
grid.setAdapter(adapter)
|
||||
findViewById<TextView>(R.id.LoadingBlankText).visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDocumentOperation(position: Int) {
|
||||
AlertHelper.showItemAlert(this,
|
||||
onCopy = {
|
||||
val file = File(documentList[position])
|
||||
if (file.isFile) {
|
||||
ClipHelper.getInstance(this).copy(file, this)
|
||||
}
|
||||
},
|
||||
onPaste = {
|
||||
val uri = ClipHelper.getInstance(this).paste()
|
||||
if (uri != null) {
|
||||
val dir = File(pasteDir)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdir()
|
||||
}
|
||||
val name = uri.path?.split('/')?.last() ?: "somePastedItem"
|
||||
val ext = name.split('.').last()
|
||||
if (!"$ext.".matches(regex)){
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return@showItemAlert
|
||||
}
|
||||
val actualFile = File(dir, "${name}_paste.$ext")
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
if (inputStream != null) {
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
// 刷新
|
||||
update()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
onDelete = {
|
||||
AlertHelper.showDeleteAlert(this, documentList[position]) {
|
||||
update()
|
||||
}
|
||||
},
|
||||
onCut = {
|
||||
CutHelper.cut(this, File(documentList[position]))
|
||||
update()
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(this, documentList[position])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSortOptions() {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
// 创建对话框构建者
|
||||
val builder = Builder(this@document_page)
|
||||
builder.setTitle("选择排序方式")
|
||||
.setItems(
|
||||
arrayOf<CharSequence>("按时间排序(默认)", "按大小排序")
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
listOrderType = 0
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@document_page,
|
||||
"已选择按时间排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
listOrderType = 1
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@document_page,
|
||||
"已选择按大小排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(
|
||||
"取消"
|
||||
) { dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun update(runSomethingMore: (()->Unit)? = null) {
|
||||
instance.initialize {
|
||||
documentList = when (listOrderType) {
|
||||
0 -> instance.dateOrderedList()
|
||||
1 -> instance.sizeOrderedList()
|
||||
else -> listOf()
|
||||
}
|
||||
val models = ArrayList<DocumentModel>()
|
||||
for (path in documentList) {
|
||||
models.add(DocumentModel(File(path)))
|
||||
}
|
||||
val adapter = DocumentAdapter(this, models)
|
||||
runOnUiThread {
|
||||
val grid = findViewById<GridView>(R.id.DocumentGrid)
|
||||
grid.setAdapter(adapter)
|
||||
}
|
||||
runSomethingMore?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadingText(
|
||||
loadingTextView: TextView,
|
||||
defaultText: CharSequence,
|
||||
dots: String = ""
|
||||
) {
|
||||
Thread.sleep(500)
|
||||
val next = if (dots.length > 3) {
|
||||
""
|
||||
} else {
|
||||
"$dots."
|
||||
}
|
||||
runOnUiThread {
|
||||
loadingTextView.text = "$defaultText$next"
|
||||
}
|
||||
|
||||
if (loadingTextView.visibility != View.GONE) {
|
||||
loadingText(loadingTextView, defaultText, next)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class document_page_search extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.document_page_search);
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
ImageView leftArrowImageView = findViewById(R.id.leftArrowImageView);
|
||||
leftArrowImageView.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(document_page_search.this, document_page.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
});
|
||||
SearchView searchView = findViewById(R.id.searchDocument); // 确保使用正确的 ID
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// 处理搜索提交
|
||||
Toast.makeText(document_page_search.this, "搜索: " + query, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
// 处理搜索文本变化
|
||||
// 可以在这里添加过滤逻辑
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.example.myapplication.fileSystem
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import com.example.myapplication.compose.PasteHelper
|
||||
import com.example.myapplication.utils.ClipHelper
|
||||
import java.io.File
|
||||
|
||||
class DeleteHelper {
|
||||
companion object{
|
||||
fun delete(path: String){
|
||||
val file = File(path)
|
||||
if (file.isFile){
|
||||
file.delete()
|
||||
}else if (file.isDirectory){
|
||||
file.deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CutHelper{
|
||||
companion object {
|
||||
fun cut(context: Context, file: File){
|
||||
val cacheDir = File("${Environment.getExternalStorageDirectory()}/.copy")
|
||||
if (!cacheDir.exists()){
|
||||
cacheDir.mkdir()
|
||||
}
|
||||
|
||||
if (file.exists()) {
|
||||
val tempFile = File("${Environment.getExternalStorageDirectory()}/.copy", file.name)
|
||||
val bytes = file.readBytes()
|
||||
tempFile.writeBytes(bytes)
|
||||
ClipHelper.getInstance(context).copy(tempFile, context)
|
||||
DeleteHelper.delete(file.path)
|
||||
}
|
||||
}
|
||||
|
||||
fun cutFolder(context: Context,folder: File){
|
||||
val cacheDir = File("${Environment.getExternalStorageDirectory()}/.copy")
|
||||
if (!cacheDir.exists()){
|
||||
cacheDir.mkdir()
|
||||
}
|
||||
|
||||
if (folder.exists()) {
|
||||
val tempFolder = File("${Environment.getExternalStorageDirectory()}/.copy", folder.name)
|
||||
PasteHelper.copyDirectory(folder,tempFolder)
|
||||
ClipHelper.getInstance(context).copyFolder(tempFolder.path)
|
||||
DeleteHelper.delete(folder.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.example.myapplication.fileSystem
|
||||
|
||||
import android.app.usage.StorageStatsManager
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.os.storage.StorageManager
|
||||
import java.io.IOException
|
||||
|
||||
class SystemStorageInfo(private val context: Context) {
|
||||
private val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
||||
private val storageStatsManager =
|
||||
context.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
|
||||
|
||||
private var totalStorage = 1uL
|
||||
private var freeStorage = 0uL
|
||||
|
||||
fun getTotalStorageSize(): ULong {
|
||||
try {
|
||||
val uuid = storageManager.getUuidForPath(Environment.getDataDirectory())
|
||||
totalStorage = storageStatsManager.getTotalBytes(uuid).toULong()
|
||||
return totalStorage
|
||||
} catch (e: IOException) {
|
||||
return 1uL
|
||||
}
|
||||
}
|
||||
|
||||
fun getFreeStorageSize(): ULong {
|
||||
try {
|
||||
val uuid = storageManager.getUuidForPath(Environment.getDataDirectory())
|
||||
freeStorage = storageStatsManager.getFreeBytes(uuid).toULong()
|
||||
return freeStorage
|
||||
} catch (e: IOException) {
|
||||
return 0uL
|
||||
}
|
||||
}
|
||||
|
||||
fun getUsedPercentage(): Int = 100 - Math.round(freeStorage.toDouble() * 100 / totalStorage.toDouble()).toInt()
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package com.example.myapplication.fileSystem
|
||||
|
||||
import android.content.Context
|
||||
import android.icu.text.DecimalFormat
|
||||
import android.text.format.DateFormat
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.VideoLister
|
||||
import java.io.File
|
||||
import java.net.URLConnection
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.sql.Date
|
||||
import java.sql.Timestamp
|
||||
import java.time.Instant
|
||||
|
||||
class WrappedFile(private val f: File, skipCalculateDirectorySize: Boolean = false) {
|
||||
companion object {
|
||||
fun getSizeString(size: ULong): String {
|
||||
var sizeFirst = size
|
||||
var sizeLast = 0
|
||||
var unit = SizeUnit.B
|
||||
|
||||
while (sizeFirst > 1024u && unit != SizeUnit.GB) {
|
||||
sizeLast = size.mod(1024u).toInt()
|
||||
sizeFirst /= 1024u
|
||||
unit = when (unit) {
|
||||
SizeUnit.B -> SizeUnit.KB
|
||||
SizeUnit.KB -> SizeUnit.MB
|
||||
SizeUnit.MB -> SizeUnit.GB
|
||||
else -> SizeUnit.B
|
||||
}
|
||||
}
|
||||
val s: Double = sizeFirst.toDouble() + sizeLast / 1000.0
|
||||
return "${DecimalFormat("#.##").format(s)} ${getUnit(unit)}"
|
||||
}
|
||||
|
||||
private fun getUnit(unit: SizeUnit): String = when (unit) {
|
||||
SizeUnit.B -> "B"
|
||||
SizeUnit.KB -> "KB"
|
||||
SizeUnit.MB -> "MB"
|
||||
SizeUnit.GB -> "GB"
|
||||
}
|
||||
|
||||
fun guessMime(ext: String): String {
|
||||
// Known ext
|
||||
val dotExt = ".$ext"
|
||||
if (dotExt.matches(ImageLister.regex)) {
|
||||
return "image/*"
|
||||
} else if (dotExt.matches(VideoLister.regex)) {
|
||||
return "video/*"
|
||||
} else if (dotExt.matches(MusicLister.regex)) {
|
||||
return "audio/*"
|
||||
} else if (dotExt.matches(DocumentLister.regex)) {
|
||||
return when (ext) {
|
||||
"xls" -> "application/vnd.ms-excel"
|
||||
"xlsx" -> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
"doc" -> "application/msword"
|
||||
"docx" -> "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
"ppt" -> "application/vnd.ms-powerpoint"
|
||||
"pptx" -> "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||
"txt" -> "text/plain"
|
||||
"htm", "html" -> "text/html"
|
||||
"pdf" -> "application/pdf"
|
||||
else -> "application/*"
|
||||
}
|
||||
} else return URLConnection.guessContentTypeFromName("f.$ext") ?: "text/plain"
|
||||
}
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
FILE,
|
||||
DIRECTORY
|
||||
}
|
||||
|
||||
private enum class SizeUnit {
|
||||
B,
|
||||
KB,
|
||||
MB,
|
||||
GB
|
||||
}
|
||||
|
||||
val name: String
|
||||
val nameWithoutExt: String
|
||||
val path: String
|
||||
val type: Type
|
||||
val lastModifiedTime: Instant
|
||||
val mime: String
|
||||
|
||||
private var isSizeCalculated = true
|
||||
var size: Long
|
||||
private set
|
||||
|
||||
init {
|
||||
if (!f.exists()) {
|
||||
throw RuntimeException("Cannot find file")
|
||||
}
|
||||
name = f.name
|
||||
nameWithoutExt = f.nameWithoutExtension
|
||||
path = f.path
|
||||
type = when (f.isDirectory) {
|
||||
true -> Type.DIRECTORY
|
||||
false -> Type.FILE
|
||||
}
|
||||
|
||||
size = if (type == Type.FILE) {
|
||||
f.length()
|
||||
} else {
|
||||
if (skipCalculateDirectorySize) {
|
||||
isSizeCalculated = false
|
||||
0
|
||||
} else {
|
||||
getFolderSize(f)
|
||||
}
|
||||
}
|
||||
|
||||
val attr: BasicFileAttributes = Files.readAttributes(
|
||||
f.toPath(),
|
||||
BasicFileAttributes::class.java
|
||||
)
|
||||
lastModifiedTime = attr.lastModifiedTime().toInstant()
|
||||
|
||||
mime = if (f.isDirectory){
|
||||
"dir"
|
||||
}else {
|
||||
guessMime(f.extension)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSizeString(): String {
|
||||
if (size == 0L) {
|
||||
if (type == Type.DIRECTORY) {
|
||||
if (!isSizeCalculated){
|
||||
// Calculate Size
|
||||
size = getFolderSize(f)
|
||||
isSizeCalculated = true
|
||||
}else{
|
||||
return "0B"
|
||||
}
|
||||
} else {
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
return getSizeString(size.toULong())
|
||||
}
|
||||
|
||||
fun getModifiedTimeString(context: Context): String {
|
||||
val ts = Timestamp.from(lastModifiedTime)
|
||||
val df = DateFormat.getDateFormat(context)
|
||||
val tf = DateFormat.getTimeFormat(context)
|
||||
val date = Date(ts.time)
|
||||
return df.format(date) + " " + tf.format(date)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.example.myapplication.fileSystem.byTypeFileLister
|
||||
|
||||
import android.os.Environment
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class DocumentLister : Lister() {
|
||||
companion object {
|
||||
val instance by lazy { DocumentLister() }
|
||||
val directories = listOf("Documents", "Download")
|
||||
val regex = "\\.((xls|doc|ppt)(x|)|txt|htm(l|)|pdf)".toRegex()
|
||||
}
|
||||
|
||||
val documentList = mutableListOf<String>()
|
||||
|
||||
fun initialize(onFinished: (() -> Unit)? = null) {
|
||||
documentList.clear()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
directories.forEach { dir ->
|
||||
walkDir(
|
||||
File("${Environment.getExternalStorageDirectory().path}/${dir}"),
|
||||
documentList,
|
||||
regex
|
||||
)
|
||||
}
|
||||
onFinished?.invoke()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fun dateOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
documentList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.lastModifiedTime }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun sizeOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
documentList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.size }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun getFullSize(): ULong {
|
||||
var size = 0UL
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
documentList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.forEach { size += it.size.toUInt() }
|
||||
return size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.example.myapplication.fileSystem.byTypeFileLister
|
||||
|
||||
import android.os.Environment
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class ImageLister private constructor() : Lister() {
|
||||
companion object {
|
||||
val instance by lazy { ImageLister() }
|
||||
val directories = listOf("DCIM", "Pictures", "Download")
|
||||
val regex = "\\.(jpg|png|jpeg|webp)".toRegex()
|
||||
}
|
||||
|
||||
val imageList = mutableListOf<String>()
|
||||
|
||||
fun initialize(onFinished: (() -> Unit)? = null) {
|
||||
imageList.clear()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
directories.forEach { imageDirectory ->
|
||||
walkDir(
|
||||
File("${Environment.getExternalStorageDirectory().path}/${imageDirectory}"),
|
||||
imageList,
|
||||
regex
|
||||
)
|
||||
}
|
||||
onFinished?.invoke()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fun dateOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
imageList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.lastModifiedTime }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun sizeOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
imageList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.size }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun getFullSize(): ULong {
|
||||
var size = 0UL
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
imageList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.forEach { size += it.size.toUInt() }
|
||||
return size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.example.myapplication.fileSystem.byTypeFileLister
|
||||
|
||||
abstract class Lister {
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.example.myapplication.fileSystem.byTypeFileLister
|
||||
|
||||
import android.os.Environment
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class MusicLister : Lister() {
|
||||
companion object {
|
||||
val instance by lazy { MusicLister() }
|
||||
val directories = listOf("Recordings", "Download", "Audiobooks", "Music", "Podcasts", "Ringtones")
|
||||
val regex = "\\.(mp3|ogg|aac|wav)".toRegex()
|
||||
}
|
||||
|
||||
val musicList = mutableListOf<String>()
|
||||
|
||||
fun initialize(onFinished: (() -> Unit)? = null) {
|
||||
musicList.clear()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
directories.forEach { dir ->
|
||||
walkDir(
|
||||
File("${Environment.getExternalStorageDirectory().path}/${dir}"),
|
||||
musicList,
|
||||
regex
|
||||
)
|
||||
}
|
||||
onFinished?.invoke()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fun dateOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
musicList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.lastModifiedTime }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun sizeOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
musicList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.size }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun getFullSize(): ULong {
|
||||
var size = 0UL
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
musicList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.forEach { size += it.size.toUInt() }
|
||||
return size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.example.myapplication.fileSystem.byTypeFileLister
|
||||
|
||||
import android.os.Environment
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class VideoLister : Lister() {
|
||||
companion object {
|
||||
val instance by lazy { VideoLister() }
|
||||
val directories = listOf("DCIM", "Download", "Movies")
|
||||
val regex = "\\.(mp4|avi|video|webm)".toRegex()
|
||||
}
|
||||
|
||||
val videoList = mutableListOf<String>()
|
||||
|
||||
fun initialize(onFinished: (() -> Unit)? = null) {
|
||||
videoList.clear()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
directories.forEach { dir ->
|
||||
walkDir(
|
||||
File("${Environment.getExternalStorageDirectory().path}/${dir}"),
|
||||
videoList,
|
||||
regex
|
||||
)
|
||||
}
|
||||
onFinished?.invoke()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fun dateOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
videoList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.lastModifiedTime }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun sizeOrderedList(): List<String> {
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
videoList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.sortBy { it.size }
|
||||
val result = mutableListOf<String>()
|
||||
wrappedFileList.forEach { result.add(it.path) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun getFullSize(): ULong {
|
||||
var size = 0UL
|
||||
val wrappedFileList = mutableListOf<WrappedFile>()
|
||||
videoList.forEach { wrappedFileList.add(WrappedFile(File(it))) }
|
||||
wrappedFileList.forEach { size += it.size.toUInt() }
|
||||
return size
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.example.myapplication.fileSystem.byTypeFileLister
|
||||
|
||||
import java.io.File
|
||||
|
||||
fun Lister.walkDir(directory: File,list: MutableList<String>,pattern: Regex ,ignoreDotFile: Boolean = true){
|
||||
if (!directory.exists() || !directory.isDirectory){
|
||||
return
|
||||
}
|
||||
directory.listFiles()?.forEach {
|
||||
if (ignoreDotFile && !it.name.startsWith(".")) {
|
||||
if (it.isDirectory) {
|
||||
walkDir(it, list, pattern)
|
||||
} else if (it.isFile) {
|
||||
if (it.name.contains(pattern)) {
|
||||
list.add(it.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.example.myapplication.fileSystem
|
||||
|
||||
import java.io.File
|
||||
|
||||
fun getFolderSize(folder: File, level: Int = 0): Long {
|
||||
if (!folder.exists() || !folder.isDirectory) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (level >= 5) { // 限制递归层数防止过度递归
|
||||
return 1.shl(63)
|
||||
}
|
||||
|
||||
var size = 0L
|
||||
|
||||
val file = folder.listFiles()
|
||||
folder.listFiles()?.forEach { content ->
|
||||
size += if (content.isFile) {
|
||||
content.length()
|
||||
} else if (content.isDirectory) {
|
||||
getFolderSize(content, level + 1).let {
|
||||
if (size > 0 || it > 0) {
|
||||
it
|
||||
} else {
|
||||
-it
|
||||
}
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
129
app/src/main/java/com/example/myapplication/main_page.java
Normal file
129
app/src/main/java/com/example/myapplication/main_page.java
Normal file
|
@ -0,0 +1,129 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import com.example.myapplication.compose.ViewFileActivity;
|
||||
import com.example.myapplication.fileSystem.DeleteHelper;
|
||||
import java.io.File;
|
||||
|
||||
public class main_page extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.main_page);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
ButtonClickerHandler buttonClickerHandler = new ButtonClickerHandler(this);
|
||||
|
||||
findViewById(R.id.MainPagePictureButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageVideoButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageMusicButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageDocumentButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageStorageButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageDownloadButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageAllFilesButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageDownloadButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageDocumentsButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageRecordingButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPageDCIMButton).setOnClickListener(buttonClickerHandler);
|
||||
findViewById(R.id.MainPagePicturesButton).setOnClickListener(buttonClickerHandler);
|
||||
}
|
||||
|
||||
@Override protected void onDestroy() {
|
||||
// 把剪切的缓存文件删掉
|
||||
File cacheDir = new File(Environment.getExternalStorageDirectory() + "/.copy");
|
||||
if (cacheDir.exists()) {
|
||||
DeleteHelper.Companion.delete(cacheDir.getPath());
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public class ButtonClickerHandler implements View.OnClickListener {
|
||||
private final Context context;
|
||||
|
||||
ButtonClickerHandler(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (view.getId() == R.id.MainPagePictureButton) {
|
||||
Intent intent = new Intent(context, picture_page.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageVideoButton) {
|
||||
Intent intent = new Intent(context, video_page.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageMusicButton) {
|
||||
Intent intent = new Intent(context, music_page.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageDocumentButton) {
|
||||
Intent intent = new Intent(context, document_page.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageStorageButton) {
|
||||
Intent intent = new Intent(context, store_page.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageAllFilesButton) {
|
||||
Intent intent = new Intent(context, ViewFileActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageDCIMButton) {
|
||||
Intent intent = new Intent(context, ViewFileActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("folder", Environment.getExternalStorageDirectory().getPath() + "/DCIM");
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageDownloadButton) {
|
||||
Intent intent = new Intent(context, ViewFileActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("folder",
|
||||
Environment.getExternalStorageDirectory().getPath() + "/Download");
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageDocumentsButton) {
|
||||
Intent intent = new Intent(context, ViewFileActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("folder",
|
||||
Environment.getExternalStorageDirectory().getPath() + "/Documents");
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPageRecordingButton) {
|
||||
Intent intent = new Intent(context, ViewFileActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("folder",
|
||||
Environment.getExternalStorageDirectory().getPath() + "/Recordings");
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
if (view.getId() == R.id.MainPagePicturesButton) {
|
||||
Intent intent = new Intent(context, ViewFileActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("folder",
|
||||
Environment.getExternalStorageDirectory().getPath() + "/Pictures");
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
269
app/src/main/java/com/example/myapplication/music_page.kt
Normal file
269
app/src/main/java/com/example/myapplication/music_page.kt
Normal file
|
@ -0,0 +1,269 @@
|
|||
package com.example.myapplication
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.GridView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AlertDialog.Builder
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type
|
||||
import com.example.myapplication.adapters.MusicAdapter
|
||||
import com.example.myapplication.adapters.MusicModel
|
||||
import com.example.myapplication.fileSystem.CutHelper
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister.Companion
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister.Companion.instance
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister.Companion.regex
|
||||
import com.example.myapplication.utils.AlertHelper
|
||||
import com.example.myapplication.utils.ClipHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
|
||||
class music_page : AppCompatActivity() {
|
||||
private var musicList = listOf<String>()
|
||||
private val pasteDir = "${Environment.getExternalStorageDirectory().path}/Music/pasted"
|
||||
private var listOrderType = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
setContentView(R.layout.music_page)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById(R.id.main)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
val systemBars =
|
||||
insets.getInsets(Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
// 设置排序按钮的点击事件
|
||||
val sortImageView = findViewById<ImageView>(R.id.sortMusicView)
|
||||
sortImageView.setOnClickListener { v: View? -> showSortOptions() }
|
||||
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
val leftArrowImageView = findViewById<ImageView>(R.id.leftArrowImageView)
|
||||
leftArrowImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@music_page,
|
||||
main_page::class.java
|
||||
)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
val searchImageView = findViewById<ImageView>(R.id.searchMusicView)
|
||||
searchImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@music_page,
|
||||
music_page_search::class.java
|
||||
)
|
||||
startActivity(intent) // 跳转到搜索页面
|
||||
}
|
||||
|
||||
findViewById<ImageView>(R.id.refreshData).setOnClickListener { _ ->
|
||||
update()
|
||||
}
|
||||
|
||||
val musicGrid: GridView = findViewById(R.id.MusicGrid)
|
||||
|
||||
musicGrid.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
val file = File(musicList[position])
|
||||
if (file.isFile) {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
this,
|
||||
applicationContext.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "audio/*")
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
musicGrid.onItemLongClickListener =
|
||||
AdapterView.OnItemLongClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
showMusicOperation(
|
||||
position
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
// 最后在后台刷新
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
val defaultText = loadingTextView.text
|
||||
launch { loadingText(loadingTextView,defaultText) }
|
||||
musicList = instance.dateOrderedList()
|
||||
val models = ArrayList<MusicModel>()
|
||||
for (path in musicList) {
|
||||
models.add(MusicModel(File(path)))
|
||||
}
|
||||
runOnUiThread {
|
||||
val adapter = MusicAdapter(this@music_page, models)
|
||||
val grid = findViewById<GridView>(R.id.MusicGrid)
|
||||
grid.setAdapter(adapter)
|
||||
findViewById<TextView>(R.id.LoadingBlankText).visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMusicOperation(position: Int) {
|
||||
AlertHelper.showItemAlert(this,
|
||||
onCopy = {
|
||||
val file = File(musicList[position])
|
||||
if (file.isFile) {
|
||||
ClipHelper.getInstance(this).copy(file, this)
|
||||
}
|
||||
},
|
||||
onPaste = {
|
||||
val uri = ClipHelper.getInstance(this).paste()
|
||||
if (uri != null) {
|
||||
val dir = File(pasteDir)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdir()
|
||||
}
|
||||
val name = uri.path?.split('/')?.last() ?: "somePastedItem"
|
||||
val ext = name.split('.').last()
|
||||
if (!"$ext.".matches(DocumentLister.regex)){
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return@showItemAlert
|
||||
}
|
||||
val actualFile = File(dir, "${name}_paste.$ext")
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
if (inputStream != null) {
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
// 刷新
|
||||
update()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
onDelete = {
|
||||
AlertHelper.showDeleteAlert(this, musicList[position]) {
|
||||
update()
|
||||
}
|
||||
},
|
||||
onCut = {
|
||||
CutHelper.cut(this, File(musicList[position]))
|
||||
update()
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(this, musicList[position])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSortOptions() {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
// 创建对话框构建者
|
||||
val builder = Builder(this@music_page)
|
||||
builder.setTitle("选择排序方式")
|
||||
.setItems(
|
||||
arrayOf<CharSequence>("按时间排序(默认)", "按大小排序")
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
listOrderType = 0
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@music_page,
|
||||
"已选择按时间排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
listOrderType = 1
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@music_page,
|
||||
"已选择按大小排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(
|
||||
"取消"
|
||||
) { dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun update(runSomethingMore: (()->Unit)? = null) {
|
||||
instance.initialize {
|
||||
musicList = when (listOrderType) {
|
||||
0 -> instance.dateOrderedList()
|
||||
1 -> instance.sizeOrderedList()
|
||||
else -> listOf()
|
||||
}
|
||||
val models = ArrayList<MusicModel>()
|
||||
for (path in musicList) {
|
||||
models.add(MusicModel(File(path)))
|
||||
}
|
||||
val adapter = MusicAdapter(this, models)
|
||||
runOnUiThread {
|
||||
val grid = findViewById<GridView>(R.id.MusicGrid)
|
||||
grid.setAdapter(adapter)
|
||||
}
|
||||
runSomethingMore?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadingText(
|
||||
loadingTextView: TextView,
|
||||
defaultText: CharSequence,
|
||||
dots: String = ""
|
||||
) {
|
||||
Thread.sleep(500)
|
||||
val next = if (dots.length > 3) {
|
||||
""
|
||||
} else {
|
||||
"$dots."
|
||||
}
|
||||
runOnUiThread {
|
||||
loadingTextView.text = "$defaultText$next"
|
||||
}
|
||||
|
||||
if (loadingTextView.visibility != View.GONE) {
|
||||
loadingText(loadingTextView, defaultText, next)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class music_page_search extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.music_page_search);
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
ImageView leftArrowImageView = findViewById(R.id.leftArrowImageView);
|
||||
leftArrowImageView.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(music_page_search.this, music_page.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
});
|
||||
SearchView searchView = findViewById(R.id.searchMusic); // 确保使用正确的 ID
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// 处理搜索提交
|
||||
Toast.makeText(music_page_search.this, "搜索: " + query, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
// 处理搜索文本变化
|
||||
// 可以在这里添加过滤逻辑
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
270
app/src/main/java/com/example/myapplication/picture_page.kt
Normal file
270
app/src/main/java/com/example/myapplication/picture_page.kt
Normal file
|
@ -0,0 +1,270 @@
|
|||
package com.example.myapplication
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.GridView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AlertDialog.Builder
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type
|
||||
import com.example.myapplication.adapters.ImageAdapter
|
||||
import com.example.myapplication.adapters.ImageModel
|
||||
import com.example.myapplication.fileSystem.CutHelper
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister.Companion
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister.Companion.instance
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister.Companion.regex
|
||||
import com.example.myapplication.utils.AlertHelper
|
||||
import com.example.myapplication.utils.ClipHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
|
||||
class picture_page : AppCompatActivity() {
|
||||
private var imageList = listOf<String>()
|
||||
private val pasteDir = "${Environment.getExternalStorageDirectory().path}/Pictures/pasted"
|
||||
private var imageListOrderType = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
setContentView(R.layout.picture_page)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById(R.id.main)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
val systemBars =
|
||||
insets.getInsets(Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
// 设置排序按钮的点击事件
|
||||
val sortImageView = findViewById<ImageView>(R.id.sortImageView)
|
||||
sortImageView.setOnClickListener { v: View? -> showSortOptions() }
|
||||
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
val leftArrowImageView = findViewById<ImageView>(R.id.leftArrowImageView)
|
||||
leftArrowImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@picture_page,
|
||||
main_page::class.java
|
||||
)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
// 设置搜索按钮的点击事件,跳转到 picture_page_search 页面
|
||||
val searchImageView = findViewById<ImageView>(R.id.searchImageView)
|
||||
searchImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@picture_page,
|
||||
picture_page_search::class.java
|
||||
)
|
||||
startActivity(intent) // 跳转到搜索页面
|
||||
}
|
||||
|
||||
findViewById<ImageView>(R.id.refreshData).setOnClickListener { _ ->
|
||||
update()
|
||||
}
|
||||
|
||||
val pictureGrid: GridView = findViewById(R.id.PicturePageGrid)
|
||||
|
||||
pictureGrid.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
val file = File(imageList[position])
|
||||
if (file.isFile) {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
this,
|
||||
applicationContext.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "image/*")
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
pictureGrid.onItemLongClickListener =
|
||||
AdapterView.OnItemLongClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
showImageOperation(
|
||||
position
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
// 最后在后台刷新
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
val defaultText = loadingTextView.text
|
||||
launch { loadingText(loadingTextView,defaultText) }
|
||||
imageList = instance.dateOrderedList()
|
||||
val imageModels = ArrayList<ImageModel>()
|
||||
for (path in imageList) {
|
||||
imageModels.add(ImageModel(File(path)))
|
||||
}
|
||||
runOnUiThread {
|
||||
val adapter = ImageAdapter(this@picture_page, imageModels)
|
||||
val grid = findViewById<GridView>(R.id.PicturePageGrid)
|
||||
grid.setAdapter(adapter)
|
||||
findViewById<TextView>(R.id.LoadingBlankText).visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showImageOperation(position: Int) {
|
||||
AlertHelper.showItemAlert(this,
|
||||
onCopy = {
|
||||
val file = File(imageList[position])
|
||||
if (file.isFile) {
|
||||
ClipHelper.getInstance(this).copy(file, this)
|
||||
}
|
||||
},
|
||||
onPaste = {
|
||||
val uri = ClipHelper.getInstance(this).paste()
|
||||
if (uri != null) {
|
||||
val dir = File(pasteDir)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdir()
|
||||
}
|
||||
val name = uri.path?.split('/')?.last() ?: "somePastedItem"
|
||||
val ext = name.split('.').last()
|
||||
if (!"$ext.".matches(DocumentLister.regex)){
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return@showItemAlert
|
||||
}
|
||||
val actualFile = File(dir, "${name}_paste.$ext")
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
if (inputStream != null) {
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
// 刷新
|
||||
update()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
onDelete = {
|
||||
AlertHelper.showDeleteAlert(this, imageList[position]) {
|
||||
update()
|
||||
}
|
||||
},
|
||||
onCut = {
|
||||
CutHelper.cut(this, File(imageList[position]))
|
||||
update()
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(this, imageList[position])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSortOptions() {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
// 创建对话框构建者
|
||||
val builder = Builder(this@picture_page)
|
||||
builder.setTitle("选择排序方式")
|
||||
.setItems(
|
||||
arrayOf<CharSequence>("按时间排序(默认)", "按大小排序")
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
imageListOrderType = 0
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@picture_page,
|
||||
"已选择按时间排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
imageListOrderType = 1
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@picture_page,
|
||||
"已选择按大小排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(
|
||||
"取消"
|
||||
) { dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun update(runSomethingMore: (()->Unit)? = null) {
|
||||
instance.initialize {
|
||||
imageList = when (imageListOrderType) {
|
||||
0 -> instance.dateOrderedList()
|
||||
1 -> instance.sizeOrderedList()
|
||||
else -> listOf()
|
||||
}
|
||||
val imageModels = ArrayList<ImageModel>()
|
||||
for (path in imageList) {
|
||||
imageModels.add(ImageModel(File(path)))
|
||||
}
|
||||
val adapter = ImageAdapter(this, imageModels)
|
||||
runOnUiThread {
|
||||
val grid = findViewById<GridView>(R.id.PicturePageGrid)
|
||||
grid.setAdapter(adapter)
|
||||
}
|
||||
runSomethingMore?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadingText(
|
||||
loadingTextView: TextView,
|
||||
defaultText: CharSequence,
|
||||
dots: String = ""
|
||||
) {
|
||||
Thread.sleep(500)
|
||||
val next = if (dots.length > 3) {
|
||||
""
|
||||
} else {
|
||||
"$dots."
|
||||
}
|
||||
runOnUiThread {
|
||||
loadingTextView.text = "$defaultText$next"
|
||||
}
|
||||
|
||||
if (loadingTextView.visibility != View.GONE) {
|
||||
loadingText(loadingTextView, defaultText, next)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class picture_page_search extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.picture_page_search);
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
ImageView leftArrowImageView = findViewById(R.id.leftArrowImageView);
|
||||
leftArrowImageView.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(picture_page_search.this, picture_page.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
});
|
||||
SearchView searchView = findViewById(R.id.searchPicture); // 确保使用正确的 ID
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// 处理搜索提交
|
||||
Toast.makeText(picture_page_search.this, "搜索: " + query, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
// 处理搜索文本变化
|
||||
// 可以在这里添加过滤逻辑
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
72
app/src/main/java/com/example/myapplication/store_page.kt
Normal file
72
app/src/main/java/com/example/myapplication/store_page.kt
Normal file
|
@ -0,0 +1,72 @@
|
|||
package com.example.myapplication
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.example.myapplication.fileSystem.SystemStorageInfo
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.ImageLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.MusicLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.VideoLister
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class store_page : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
setContentView(R.layout.store_page)
|
||||
val leftArrowImageView = findViewById<ImageView>(R.id.leftArrowImageView)
|
||||
leftArrowImageView.setOnClickListener { v: View? ->
|
||||
val intent = Intent(
|
||||
this@store_page,
|
||||
main_page::class.java
|
||||
)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
val systemStorageInfo = SystemStorageInfo(this)
|
||||
val tv = findViewById<TextView>(R.id.storageText)
|
||||
tv.text = getString(
|
||||
R.string.used_storage,
|
||||
WrappedFile.getSizeString(systemStorageInfo.getTotalStorageSize() - systemStorageInfo.getFreeStorageSize()),
|
||||
WrappedFile.getSizeString(systemStorageInfo.getTotalStorageSize())
|
||||
)
|
||||
(findViewById<View>(R.id.progressText) as TextView).text =
|
||||
getString(
|
||||
R.string.used_storage_percentage,
|
||||
systemStorageInfo.getUsedPercentage()
|
||||
)
|
||||
(findViewById<View>(R.id.progressBar) as ProgressBar).progress =
|
||||
systemStorageInfo.getUsedPercentage()
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val imageSize = ImageLister.instance.getFullSize()
|
||||
val imageSizeString = WrappedFile.getSizeString(imageSize)
|
||||
val videoSize = VideoLister.instance.getFullSize()
|
||||
val videoSizeString = WrappedFile.getSizeString(videoSize)
|
||||
val musicSize = MusicLister.instance.getFullSize()
|
||||
val musicSizeString = WrappedFile.getSizeString(musicSize)
|
||||
val documentSize = DocumentLister.instance.getFullSize()
|
||||
val documentSizeString = WrappedFile.getSizeString(documentSize)
|
||||
val otherSize = systemStorageInfo.getTotalStorageSize() - systemStorageInfo.getFreeStorageSize() - imageSize - musicSize - documentSize
|
||||
val otherSizeString = WrappedFile.getSizeString(otherSize)
|
||||
runOnUiThread{
|
||||
findViewById<TextView>(R.id.pictureStorage).text = imageSizeString
|
||||
findViewById<TextView>(R.id.videoStorage).text = videoSizeString
|
||||
findViewById<TextView>(R.id.audioStorage).text = musicSizeString
|
||||
findViewById<TextView>(R.id.documentStorage).text = documentSizeString
|
||||
findViewById<TextView>(R.id.appStorage).text = otherSizeString
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
167
app/src/main/java/com/example/myapplication/utils/AlertHelper.kt
Normal file
167
app/src/main/java/com/example/myapplication/utils/AlertHelper.kt
Normal file
|
@ -0,0 +1,167 @@
|
|||
package com.example.myapplication.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import androidx.appcompat.app.AlertDialog.Builder
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.fileSystem.DeleteHelper
|
||||
import com.example.myapplication.fileSystem.WrappedFile
|
||||
import java.io.File
|
||||
|
||||
class AlertHelper {
|
||||
companion object {
|
||||
fun showItemAlert(
|
||||
context: Context,
|
||||
onCopy: () -> Unit,
|
||||
onPaste: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
onCut: () -> Unit,
|
||||
onInfo: () -> Unit
|
||||
) {
|
||||
val builder = Builder(context)
|
||||
builder.setTitle(context.getString(R.string.select_action))
|
||||
.setItems(
|
||||
arrayOf<CharSequence>(
|
||||
context.getString(R.string.action_copy),
|
||||
context.getString(R.string.action_paste),
|
||||
context.getString(R.string.action_delete),
|
||||
context.getString(R.string.action_cut),
|
||||
context.getString(R.string.action_info)
|
||||
)
|
||||
) { _: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
onCopy()
|
||||
}
|
||||
|
||||
1 -> {
|
||||
onPaste()
|
||||
}
|
||||
|
||||
2 -> {
|
||||
onDelete()
|
||||
}
|
||||
|
||||
3 -> {
|
||||
onCut()
|
||||
}
|
||||
|
||||
4 -> {
|
||||
onInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(context.getString(R.string.action_cancel))
|
||||
{ dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showNoPasteAlert(
|
||||
context: Context,
|
||||
onCopy: () -> Unit,
|
||||
onPaste: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
onCut: () -> Unit,
|
||||
onInfo: () -> Unit
|
||||
) {
|
||||
val builder = Builder(context)
|
||||
builder.setTitle(context.getString(R.string.select_action))
|
||||
.setItems(
|
||||
arrayOf<CharSequence>(
|
||||
context.getString(R.string.action_copy),
|
||||
context.getString(R.string.action_delete),
|
||||
context.getString(R.string.action_cut),
|
||||
context.getString(R.string.action_info)
|
||||
)
|
||||
) { _: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
onCopy()
|
||||
}
|
||||
|
||||
1 -> {
|
||||
onDelete()
|
||||
}
|
||||
|
||||
2 -> {
|
||||
onCut()
|
||||
}
|
||||
|
||||
3 -> {
|
||||
onInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(context.getString(R.string.action_cancel))
|
||||
{ dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showOnlyPasteInfoNewAlert(
|
||||
context: Context,
|
||||
onPaste: () -> Unit,
|
||||
onInfo: () -> Unit,
|
||||
onNewFile: () -> Unit,
|
||||
onNewFolder: () -> Unit
|
||||
) {
|
||||
val builder = Builder(context)
|
||||
builder.setTitle(context.getString(R.string.select_action))
|
||||
.setItems(
|
||||
arrayOf<CharSequence>(
|
||||
context.getString(R.string.action_paste),
|
||||
context.getString(R.string.action_info),
|
||||
context.getString(R.string.action_new_file),
|
||||
context.getString(R.string.action_new_folder)
|
||||
)
|
||||
) { _: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> onPaste()
|
||||
1 -> onInfo()
|
||||
2-> onNewFile()
|
||||
3-> onNewFolder()
|
||||
}
|
||||
}
|
||||
.setNegativeButton(context.getString(R.string.action_cancel))
|
||||
{ dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showDeleteAlert(context: Context, file: String, onConfirm: (() -> Unit)?) {
|
||||
val builder = Builder(context)
|
||||
builder.setTitle(context.getString(R.string.confirm_to_delete))
|
||||
.setMessage(context.getString(R.string.confirm_to_delete_file, file))
|
||||
.setPositiveButton(context.getString(R.string.confirm)) { _, _ ->
|
||||
DeleteHelper.delete(file)
|
||||
onConfirm?.invoke()
|
||||
}
|
||||
.setNegativeButton(context.getString(R.string.cancel)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showFileInfoAlert(context: Context, file: String) {
|
||||
val f = File(file)
|
||||
if (!f.exists()) {
|
||||
return
|
||||
}
|
||||
val wrappedFile = WrappedFile(f)
|
||||
|
||||
val builder = Builder(context)
|
||||
builder.setTitle(context.getString(R.string.file_info))
|
||||
.setMessage(
|
||||
context.getString(
|
||||
R.string.file_info_text,
|
||||
wrappedFile.name,
|
||||
wrappedFile.path,
|
||||
wrappedFile.getSizeString(),
|
||||
wrappedFile.getModifiedTimeString(context)
|
||||
)
|
||||
)
|
||||
.setNegativeButton(context.getString(R.string.okay)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package com.example.myapplication.utils
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.core.content.FileProvider
|
||||
import com.example.myapplication.BuildConfig
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.internal.synchronized
|
||||
import java.io.File
|
||||
|
||||
class ClipHelper private constructor(context: Context) {
|
||||
companion object {
|
||||
private const val label = "${BuildConfig.APPLICATION_ID}\$ClipHelper"
|
||||
private const val ENCODE_LABEL = "ClipHelper"
|
||||
|
||||
private var instance: ClipHelper? = null
|
||||
|
||||
@OptIn(InternalCoroutinesApi::class)
|
||||
fun getInstance(context: Context): ClipHelper =
|
||||
instance ?: synchronized(this) {
|
||||
instance ?: ClipHelper(context).also { instance = it }
|
||||
}
|
||||
}
|
||||
|
||||
private val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
private val contentResolver: ContentResolver = context.contentResolver
|
||||
|
||||
fun copy(file: File, context: Context) {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
context.applicationContext.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
val clip = ClipData.newUri(contentResolver, label, uri)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
|
||||
fun paste(): Uri? {
|
||||
val clip = clipboard.primaryClip
|
||||
clip?.run {
|
||||
val item: ClipData.Item = getItemAt(0)
|
||||
return item.uri
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun copyFolder(folder: String){
|
||||
val clip = ClipData.newPlainText("SingleFolderCopy","$ENCODE_LABEL:${folder}")
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
|
||||
fun pasteFolder(): String? {
|
||||
val clip = clipboard.primaryClip
|
||||
val content = clip?.run {
|
||||
val item: ClipData.Item = getItemAt(0)
|
||||
item.text
|
||||
}
|
||||
if (content != null){
|
||||
if (content.startsWith(ENCODE_LABEL)){
|
||||
return content.split(':').last()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
271
app/src/main/java/com/example/myapplication/video_page.kt
Normal file
271
app/src/main/java/com/example/myapplication/video_page.kt
Normal file
|
@ -0,0 +1,271 @@
|
|||
package com.example.myapplication
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.GridView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AlertDialog.Builder
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type
|
||||
import com.example.myapplication.adapters.ImageAdapter
|
||||
import com.example.myapplication.adapters.ImageModel
|
||||
import com.example.myapplication.adapters.VideoAdapter
|
||||
import com.example.myapplication.adapters.VideoModel
|
||||
import com.example.myapplication.fileSystem.CutHelper
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.DocumentLister.Companion
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.VideoLister.Companion.regex
|
||||
import com.example.myapplication.fileSystem.byTypeFileLister.VideoLister.Companion.instance
|
||||
import com.example.myapplication.utils.AlertHelper
|
||||
import com.example.myapplication.utils.ClipHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
|
||||
class video_page : AppCompatActivity() {
|
||||
private var videoList = listOf<String>()
|
||||
private val pasteDir = "${Environment.getExternalStorageDirectory().path}/Movies/pasted"
|
||||
private var videoListOrderType = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
setContentView(R.layout.video_page)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById(R.id.main)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
val systemBars =
|
||||
insets.getInsets(Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
// 设置排序按钮的点击事件
|
||||
val sortImageView = findViewById<ImageView>(R.id.sortVideoView)
|
||||
sortImageView.setOnClickListener { v: View? -> showSortOptions() }
|
||||
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
val leftArrowImageView = findViewById<ImageView>(R.id.leftArrowImageView)
|
||||
leftArrowImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@video_page,
|
||||
main_page::class.java
|
||||
)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
val searchImageView = findViewById<ImageView>(R.id.searchVideoView)
|
||||
searchImageView.setOnClickListener { v: View? ->
|
||||
val intent =
|
||||
Intent(
|
||||
this@video_page,
|
||||
video_page_search::class.java
|
||||
)
|
||||
startActivity(intent) // 跳转到搜索页面
|
||||
}
|
||||
|
||||
findViewById<ImageView>(R.id.refreshData).setOnClickListener { _ ->
|
||||
update()
|
||||
}
|
||||
|
||||
val videoGrid: GridView = findViewById(R.id.VideoGrid)
|
||||
|
||||
videoGrid.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
val file = File(videoList[position])
|
||||
if (file.isFile) {
|
||||
val uri = FileProvider.getUriForFile(
|
||||
this,
|
||||
applicationContext.packageName + ".provider",
|
||||
file
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "video/*")
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
videoGrid.onItemLongClickListener =
|
||||
AdapterView.OnItemLongClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
|
||||
showVideoOperation(
|
||||
position
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
// 最后在后台刷新
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
val defaultText = loadingTextView.text
|
||||
launch { loadingText(loadingTextView,defaultText) }
|
||||
videoList = instance.dateOrderedList()
|
||||
val videoModels = ArrayList<VideoModel>()
|
||||
for (path in videoList) {
|
||||
videoModels.add(VideoModel(File(path)))
|
||||
}
|
||||
runOnUiThread {
|
||||
val adapter = VideoAdapter(this@video_page, videoModels)
|
||||
val grid = findViewById<GridView>(R.id.VideoGrid)
|
||||
grid.setAdapter(adapter)
|
||||
findViewById<TextView>(R.id.LoadingBlankText).visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showVideoOperation(position: Int) {
|
||||
AlertHelper.showItemAlert(this,
|
||||
onCopy = {
|
||||
val file = File(videoList[position])
|
||||
if (file.isFile) {
|
||||
ClipHelper.getInstance(this).copy(file, this)
|
||||
}
|
||||
},
|
||||
onPaste = {
|
||||
val uri = ClipHelper.getInstance(this).paste()
|
||||
if (uri != null) {
|
||||
val dir = File(pasteDir)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdir()
|
||||
}
|
||||
val name = uri.path?.split('/')?.last() ?: "somePastedItem"
|
||||
val ext = name.split('.').last()
|
||||
if (!"$ext.".matches(DocumentLister.regex)){
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return@showItemAlert
|
||||
}
|
||||
val actualFile = File(dir, "${name}_paste.$ext")
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
if (inputStream != null) {
|
||||
actualFile.writeBytes(IOUtils.toByteArray(inputStream))
|
||||
inputStream.close()
|
||||
// 刷新
|
||||
update()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.error_nothing_to_paste), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
onDelete = {
|
||||
AlertHelper.showDeleteAlert(this, videoList[position]) {
|
||||
update()
|
||||
}
|
||||
},
|
||||
onCut = {
|
||||
CutHelper.cut(this, File(videoList[position]))
|
||||
update()
|
||||
},
|
||||
onInfo = {
|
||||
AlertHelper.showFileInfoAlert(this, videoList[position])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showSortOptions() {
|
||||
val loadingTextView = findViewById<TextView>(R.id.LoadingBlankText)
|
||||
// 创建对话框构建者
|
||||
val builder = Builder(this@video_page)
|
||||
builder.setTitle("选择排序方式")
|
||||
.setItems(
|
||||
arrayOf<CharSequence>("按时间排序(默认)", "按大小排序")
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
videoListOrderType = 0
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@video_page,
|
||||
"已选择按时间排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
videoListOrderType = 1
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.VISIBLE
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch { loadingText(loadingTextView,loadingTextView.text) }
|
||||
update {
|
||||
runOnUiThread{
|
||||
loadingTextView.visibility = View.GONE
|
||||
Toast.makeText(
|
||||
this@video_page,
|
||||
"已选择按大小排序",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(
|
||||
"取消"
|
||||
) { dialog: DialogInterface, which: Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun update(runSomethingMore: (()->Unit)? = null) {
|
||||
instance.initialize {
|
||||
videoList = when (videoListOrderType) {
|
||||
0 -> instance.dateOrderedList()
|
||||
1 -> instance.sizeOrderedList()
|
||||
else -> listOf()
|
||||
}
|
||||
val videoModels = ArrayList<VideoModel>()
|
||||
for (path in videoList) {
|
||||
videoModels.add(VideoModel(File(path)))
|
||||
}
|
||||
val adapter = VideoAdapter(this, videoModels)
|
||||
runOnUiThread {
|
||||
val grid = findViewById<GridView>(R.id.VideoGrid)
|
||||
grid.setAdapter(adapter)
|
||||
}
|
||||
runSomethingMore?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadingText(
|
||||
loadingTextView: TextView,
|
||||
defaultText: CharSequence,
|
||||
dots: String = ""
|
||||
) {
|
||||
Thread.sleep(500)
|
||||
val next = if (dots.length > 3) {
|
||||
""
|
||||
} else {
|
||||
"$dots."
|
||||
}
|
||||
runOnUiThread {
|
||||
loadingTextView.text = "$defaultText$next"
|
||||
}
|
||||
|
||||
if (loadingTextView.visibility != View.GONE) {
|
||||
loadingText(loadingTextView, defaultText, next)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.example.myapplication;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class video_page_search extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.video_page_search);
|
||||
// 设置左箭头的点击事件,返回上一级页面
|
||||
ImageView leftArrowImageView = findViewById(R.id.leftArrowImageView);
|
||||
leftArrowImageView.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(video_page_search.this, video_page.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
});
|
||||
SearchView searchView = findViewById(R.id.searchVideo); // 确保使用正确的 ID
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// 处理搜索提交
|
||||
Toast.makeText(video_page_search.this, "搜索: " + query, Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
// 处理搜索文本变化
|
||||
// 可以在这里添加过滤逻辑
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
7
app/src/main/res/drawable/baseline_camera_alt_24.xml
Normal file
7
app/src/main/res/drawable/baseline_camera_alt_24.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#514A3A" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/baseline_photo_library_24.xml
Normal file
12
app/src/main/res/drawable/baseline_photo_library_24.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="40dp"
|
||||
android:tint="#BAC8B8"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="40dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z" />
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/baseline_question_mark_24.xml
Normal file
5
app/src/main/res/drawable/baseline_question_mark_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#DF7F7F" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M11.07,12.85c0.77,-1.39 2.25,-2.21 3.11,-3.44c0.91,-1.29 0.4,-3.7 -2.18,-3.7c-1.69,0 -2.52,1.28 -2.87,2.34L6.54,6.96C7.25,4.83 9.18,3 11.99,3c2.35,0 3.96,1.07 4.78,2.41c0.7,1.15 1.11,3.3 0.03,4.9c-1.2,1.77 -2.35,2.31 -2.97,3.45c-0.25,0.46 -0.35,0.76 -0.35,2.24h-2.89C10.58,15.22 10.46,13.95 11.07,12.85zM14,20c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2S14,18.9 14,20z"/>
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/baseline_refresh_24.xml
Normal file
12
app/src/main/res/drawable/baseline_refresh_24.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="40dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="40dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" />
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/baseline_settings_24.xml
Normal file
12
app/src/main/res/drawable/baseline_settings_24.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="40dp"
|
||||
android:tint="#006064"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="40dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/baseline_storage_24.xml
Normal file
5
app/src/main/res/drawable/baseline_storage_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#1A1B3F" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M2,20h20v-4L2,16v4zM4,17h2v2L4,19v-2zM2,4v4h20L22,4L2,4zM6,7L4,7L4,5h2v2zM2,14h20v-4L2,10v4zM4,11h2v2L4,13v-2z"/>
|
||||
|
||||
</vector>
|
7
app/src/main/res/drawable/bg_roundcorner.xml
Normal file
7
app/src/main/res/drawable/bg_roundcorner.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FFFFFF"/>
|
||||
<stroke android:width="3dp" android:color="@color/WhiteSmoke" />
|
||||
<corners android:radius="255dp"/>
|
||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||
</shape>
|
29
app/src/main/res/drawable/circular_progress_bar.xml
Normal file
29
app/src/main/res/drawable/circular_progress_bar.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- 背景圆环 -->
|
||||
<item android:id="@android:id/background">
|
||||
<shape
|
||||
android:shape="ring"
|
||||
android:innerRadiusRatio="3"
|
||||
android:thicknessRatio="15"
|
||||
android:useLevel="false">
|
||||
<solid android:color="#e0e0e0" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- 进度条圆环 -->
|
||||
<item android:id="@android:id/progress">
|
||||
<rotate
|
||||
android:fromDegrees="-90"
|
||||
android:toDegrees="270">
|
||||
<shape
|
||||
android:shape="ring"
|
||||
android:innerRadiusRatio="3"
|
||||
android:thicknessRatio="15"
|
||||
android:useLevel="true">
|
||||
<solid android:color="#66cc33" />
|
||||
</shape>
|
||||
</rotate>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
5
app/src/main/res/drawable/ic_app.xml
Normal file
5
app/src/main/res/drawable/ic_app.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="62.5dp" android:tint="#FF8C00" android:viewportHeight="24" android:viewportWidth="24" android:width="62.5dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_arrow_drop_down.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_drop_down.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_circle_blue.xml
Normal file
5
app/src/main/res/drawable/ic_circle_blue.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="5dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="5dp">
|
||||
|
||||
<path android:fillColor="#1296db" android:pathData="M256,512c0,70.7 25,131.1 75,181A246.6,246.6 0,0 0,512 768c70.7,0 131,-25 181,-75C743,643.1 768,582.7 768,512c0,-70.7 -25,-131.1 -75,-181A246.6,246.6 0,0 0,512 256c-70.7,0 -131,25 -181,75C281,380.9 256,441.3 256,512z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_circle_green.xml
Normal file
5
app/src/main/res/drawable/ic_circle_green.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="5dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="5dp">
|
||||
|
||||
<path android:fillColor="@color/lightgrey" android:pathData="M256,512c0,70.7 25,131.1 75,181A246.6,246.6 0,0 0,512 768c70.7,0 131,-25 181,-75C743,643.1 768,582.7 768,512c0,-70.7 -25,-131.1 -75,-181A246.6,246.6 0,0 0,512 256c-70.7,0 -131,25 -181,75C281,380.9 256,441.3 256,512z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_circle_orange.xml
Normal file
5
app/src/main/res/drawable/ic_circle_orange.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="5dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="5dp">
|
||||
|
||||
<path android:fillColor="#FF8000" android:pathData="M256,512c0,70.7 25,131.1 75,181A246.6,246.6 0,0 0,512 768c70.7,0 131,-25 181,-75C743,643.1 768,582.7 768,512c0,-70.7 -25,-131.1 -75,-181A246.6,246.6 0,0 0,512 256c-70.7,0 -131,25 -181,75C281,380.9 256,441.3 256,512z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_circle_red.xml
Normal file
5
app/src/main/res/drawable/ic_circle_red.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="5dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="5dp">
|
||||
|
||||
<path android:fillColor="#d81e06" android:pathData="M256,512c0,70.7 25,131.1 75,181A246.6,246.6 0,0 0,512 768c70.7,0 131,-25 181,-75C743,643.1 768,582.7 768,512c0,-70.7 -25,-131.1 -75,-181A246.6,246.6 0,0 0,512 256c-70.7,0 -131,25 -181,75C281,380.9 256,441.3 256,512z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_circle_yellow.xml
Normal file
5
app/src/main/res/drawable/ic_circle_yellow.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="5dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="5dp">
|
||||
|
||||
<path android:fillColor="#f4ea2a" android:pathData="M256,512c0,70.7 25,131.1 75,181A246.6,246.6 0,0 0,512 768c70.7,0 131,-25 181,-75C743,643.1 768,582.7 768,512c0,-70.7 -25,-131.1 -75,-181A246.6,246.6 0,0 0,512 256c-70.7,0 -131,25 -181,75C281,380.9 256,441.3 256,512z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_document.xml
Normal file
5
app/src/main/res/drawable/ic_document.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="62.5dp" android:tint="#FFD700" android:viewportHeight="24" android:viewportWidth="24" android:width="62.5dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,3H4v3H2V1h5V3zM22,6V1h-5v2h3v3H22zM7,21H4v-3H2v5h5V21zM20,18v3h-3v2h5v-5H20zM19,18c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2V18zM15,8H9v2h6V8zM15,11H9v2h6V11zM15,14H9v2h6V14z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_download.xml
Normal file
5
app/src/main/res/drawable/ic_download.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#FF00FF" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@color/white" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
|
||||
|
||||
</vector>
|
13
app/src/main/res/drawable/ic_install_package.xml
Normal file
13
app/src/main/res/drawable/ic_install_package.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:viewportHeight="512" android:viewportWidth="512" android:width="40dp">
|
||||
|
||||
<path android:fillColor="#94bd31" android:fillType="nonZero" android:pathData="m512,256c0,141.38 -114.62,256 -256,256 -141.38,0 -256,-114.62 -256,-256 0,-141.38 114.62,-256 256,-256 141.38,0 256,114.62 256,256z" android:strokeColor="#00000000"/>
|
||||
|
||||
<path android:fillColor="#ffffff" android:pathData="m348.11,339.48l0,-116.67 0,-13.99l0,0l0,-15.82l0.04,0 -184.08,0 -0.18,0l0,16.24 0,13.57 0,116.67c0,11.66 8.21,20.74 19.87,20.74l15.28,0c-0.53,1.42 0.28,4.03 0.28,5.95l0,1.13 0,6.67 0,34.5c0,11.05 8.08,20.03 19.13,20.03 11.05,0 19.13,-8.98 19.13,-20.03l0,-34.5 0,-6.67 0,-1.13c0,-1.91 0.43,-4.54 -0.08,-5.95l37.21,0c-0.53,1.42 -0.28,4.03 -0.28,5.95l0,1.13 0,6.67 0,34.5c0,11.05 8.08,20.03 19.13,20.03 11.07,0 19.13,-8.98 19.13,-20.03l0,-34.5 0,-6.67 0,-1.13c0,-1.91 0.99,-4.54 0.49,-5.95l15.26,0c11.68,0 19.68,-9.08 19.68,-20.74z"/>
|
||||
|
||||
<path android:fillColor="#ffffff" android:pathData="m363.7,212.61l0,85.68c0,11.07 8.07,20.03 19.15,20.03 11.06,0 19.12,-8.96 19.12,-20.03l0,-85.68c0,-11.07 -8.06,-20.03 -19.12,-20.03 -11.07,0 -19.15,8.96 -19.15,20.03z"/>
|
||||
|
||||
<path android:fillColor="#ffffff" android:pathData="m129.16,318.32c11.06,0 19.14,-8.96 19.14,-20.03l0,-85.68c0,-11.07 -8.08,-20.03 -19.14,-20.03 -11.06,0 -19.12,8.96 -19.12,20.03l0,85.68c0,11.07 8.06,20.03 19.12,20.03z"/>
|
||||
|
||||
<path android:fillColor="#ffffff" android:pathData="m309.54,101.18 l8.32,-12.49c1.03,-1.54 0.6,-3.61 -0.91,-4.63 -1.55,-1.03 -3.62,-0.61 -4.64,0.93l-8.93,13.4 -3.75,5.63 -3.81,5.71c-12.04,-4.68 -25.52,-7.3 -39.73,-7.3 -14.18,0 -27.64,2.62 -39.71,7.3l-3.81,-5.71 -3.73,-5.63 -8.93,-13.4c-1.02,-1.54 -3.1,-1.94 -4.64,-0.93 -1.54,1.01 -1.94,3.09 -0.93,4.63l8.32,12.49 3.73,5.57 3.75,5.46c-28.34,13.19 -47.51,38.28 -47.51,66.62l186.95,0c0,-28.34 -19.17,-53.44 -47.52,-66.62l3.77,-5.56 3.71,-5.47zM216.05,152.51c-5.53,0 -10.02,-4.49 -10.02,-10.02 0,-5.53 4.49,-10.02 10.02,-10.02 5.53,0 10.02,4.49 10.02,10.02 0,5.53 -4.49,10.02 -10.02,10.02zM306.19,142.49c0,5.53 -4.49,10.02 -10.02,10.02 -5.53,0 -10.02,-4.49 -10.02,-10.02 0,-5.53 4.49,-10.02 10.02,-10.02 5.53,0 10.02,4.49 10.02,10.02z"/>
|
||||
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
10
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
</vector>
|
33
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
33
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1026"
|
||||
android:viewportHeight="1024">
|
||||
<group android:scaleY="0.998004"
|
||||
android:translateY="1.0219561">
|
||||
<path
|
||||
android:pathData="M853.3,874.7H128c-46.9,0 -85.3,-38.4 -85.3,-85.3V234.7c0,-46.9 38.4,-85.3 85.3,-85.3l337.1,-2.1L597.3,42.7h256c46.9,0 85.3,38.4 85.3,85.3v661.3c0,46.9 -38.4,85.3 -85.3,85.3z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M853.3,896H128c-59.7,0 -106.7,-46.9 -106.7,-106.7V234.7c0,-59.7 46.9,-106.7 106.7,-106.7l330.7,-2.1L588.8,21.3H853.3c59.7,0 106.7,46.9 106.7,106.7v661.3c0,57.6 -49.1,106.7 -106.7,106.7zM603.7,64l-132.3,104.5L128,170.7c-36.3,0 -64,27.7 -64,64v554.7c0,36.3 27.7,64 64,64h725.3c36.3,0 64,-27.7 64,-64V128c0,-36.3 -27.7,-64 -64,-64H603.7z"/>
|
||||
<path
|
||||
android:pathData="M213.3,234.7h554.7c46.9,0 85.3,38.4 85.3,85.3v469.3H128V320c0,-49.1 38.4,-85.3 85.3,-85.3z"
|
||||
android:fillColor="#FFBF80"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M874.7,810.7H106.7V320c0,-59.7 46.9,-106.7 106.7,-106.7h554.7c59.7,0 106.7,46.9 106.7,106.7v490.7zM149.3,768h682.7V320c0,-36.3 -27.7,-64 -64,-64H213.3c-36.3,0 -64,27.7 -64,64v448z"/>
|
||||
<path
|
||||
android:pathData="M42.7,320h896v512c0,46.9 -38.4,85.3 -85.3,85.3H128c-46.9,0 -85.3,-38.4 -85.3,-85.3V320z"
|
||||
android:fillColor="#3399FF"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M853.3,938.7H128c-59.7,0 -106.7,-46.9 -106.7,-106.7V298.7h938.7v533.3c0,57.6 -49.1,106.7 -106.7,106.7zM64,341.3v490.7c0,36.3 27.7,64 64,64h725.3c36.3,0 64,-27.7 64,-64V341.3H64z"/>
|
||||
<path
|
||||
android:pathData="M789.3,979.2l-215.5,-117.3v-234.7L789.3,512l213.3,117.3v234.7l-213.3,115.2zM789.3,661.3c-46.9,0 -85.3,38.4 -85.3,85.3s38.4,85.3 85.3,85.3 85.3,-38.4 85.3,-85.3 -38.4,-85.3 -85.3,-85.3z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M789.3,1002.7l-236.8,-128L552.5,616.5l236.8,-128 236.8,128L1026.1,874.7L789.3,1002.7zM595.2,849.1l194.1,104.5 194.1,-104.5L983.5,640L789.3,535.5 595.2,640v209.1zM789.3,853.3c-59.7,0 -106.7,-46.9 -106.7,-106.7s46.9,-106.7 106.7,-106.7 106.7,46.9 106.7,106.7 -49.1,106.7 -106.7,106.7zM789.3,682.7c-36.3,0 -64,27.7 -64,64s27.7,64 64,64 64,-27.7 64,-64 -29.9,-64 -64,-64z"/>
|
||||
</group>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_left_arrow.xml
Normal file
10
app/src/main/res/drawable/ic_left_arrow.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="40dp" android:tint="#000000" android:viewportHeight="23" android:viewportWidth="25" android:width="40dp">
|
||||
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="0.005"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_music.xml
Normal file
12
app/src/main/res/drawable/ic_music.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="62.5dp"
|
||||
android:tint="#BA55D3"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="62.5dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z" />
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_no_document.xml
Normal file
5
app/src/main/res/drawable/ic_no_document.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="10dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="10dp">
|
||||
|
||||
<path android:fillColor="#E9EBED" android:pathData="M870.6,240.8L467.2,240.8c0,-49.5 -40.1,-89.6 -89.6,-89.6L153.5,151.1c-49.5,0 -89.7,40.1 -89.7,89.6v627.5c0,49.5 40.1,89.7 89.7,89.7h717.2c49.5,0 89.6,-40.1 89.6,-89.7L960.3,330.4c-0,-49.5 -40.1,-89.7 -89.7,-89.7zM870.6,823.5c0,24.8 -20.1,44.8 -44.8,44.8L198.3,868.3c-24.8,0 -44.8,-20.1 -44.8,-44.8L153.5,509.7h717.2v313.8zM825.8,420.1L153.5,420.1v-179.3c0.2,0.2 20.1,0 44.8,0h134.5c24.8,0 44.8,0.1 44.8,0v89.7L825.8,330.4c24.8,0 44.8,20.1 44.8,44.8 -0,24.8 -20.1,44.8 -44.8,44.8z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_photo.xml
Normal file
5
app/src/main/res/drawable/ic_photo.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="62.5dp" android:tint="#1E90FF" android:viewportHeight="24" android:viewportWidth="24" android:width="62.5dp">
|
||||
|
||||
<path android:fillColor="#FFD700" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_play.xml
Normal file
5
app/src/main/res/drawable/ic_play.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="70dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="70dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_radio.xml
Normal file
5
app/src/main/res/drawable/ic_radio.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21,6h-7.59l3.29,-3.29L16,2l-4,4 -4,-4 -0.71,0.71L10.59,6L3,6c-1.1,0 -2,0.89 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.11 -0.9,-2 -2,-2zM21,20L3,20L3,8h18v12zM9,10v8l7,-4z"/>
|
||||
|
||||
</vector>
|
7
app/src/main/res/drawable/ic_search.xml
Normal file
7
app/src/main/res/drawable/ic_search.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#000000" android:viewportHeight="23" android:viewportWidth="23" android:width="40dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_storage.xml
Normal file
5
app/src/main/res/drawable/ic_storage.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#8B4513" android:viewportHeight="24" android:viewportWidth="24" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,8h-2L10,4h2v4zM15,8h-2L13,4h2v4zM18,8h-2L16,4h2v4z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_video.xml
Normal file
5
app/src/main/res/drawable/ic_video.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="62.5dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="62.5dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21,3L3,3c-1.11,0 -2,0.89 -2,2v12c0,1.1 0.89,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.11 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12zM16,11l-7,4L9,7z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/music_icon.xml
Normal file
5
app/src/main/res/drawable/music_icon.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="45dp" android:tint="#CD2990" android:viewportHeight="24" android:viewportWidth="24" android:width="45dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M15,6H3v2h12V6zM15,10H3v2h12V10zM3,16h8v-2H3V16zM17,6v8.18C16.69,14.07 16.35,14 16,14c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3V8h3V6H17z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/outline_arrow_upward_32.xml
Normal file
5
app/src/main/res/drawable/outline_arrow_upward_32.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="32dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M440,800L440,313L216,537L160,480L480,160L800,480L744,537L520,313L520,800L440,800Z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/outline_info_24.xml
Normal file
5
app/src/main/res/drawable/outline_info_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="32dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M440,680L520,680L520,440L440,440L440,680ZM480,360Q497,360 508.5,348.5Q520,337 520,320Q520,303 508.5,291.5Q497,280 480,280Q463,280 451.5,291.5Q440,303 440,320Q440,337 451.5,348.5Q463,360 480,360ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/outline_info_i_24.xml
Normal file
5
app/src/main/res/drawable/outline_info_i_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="32dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M480,280Q447,280 423.5,256.5Q400,233 400,200Q400,167 423.5,143.5Q447,120 480,120Q513,120 536.5,143.5Q560,167 560,200Q560,233 536.5,256.5Q513,280 480,280ZM420,840L420,360L540,360L540,840L420,840Z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/rounded_lab_profile_24.xml
Normal file
5
app/src/main/res/drawable/rounded_lab_profile_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="40dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="40dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M360,480Q343,480 331.5,468.5Q320,457 320,440Q320,423 331.5,411.5Q343,400 360,400L600,400Q617,400 628.5,411.5Q640,423 640,440Q640,457 628.5,468.5Q617,480 600,480L360,480ZM360,320Q343,320 331.5,308.5Q320,297 320,280Q320,263 331.5,251.5Q343,240 360,240L600,240Q617,240 628.5,251.5Q640,263 640,280Q640,297 628.5,308.5Q617,320 600,320L360,320ZM240,560L540,560Q569,560 594,572.5Q619,585 636,608L720,718L720,160Q720,160 720,160Q720,160 720,160L240,160Q240,160 240,160Q240,160 240,160L240,560ZM240,800L682,800L573,657Q567,649 558.5,644.5Q550,640 540,640L240,640L240,800Q240,800 240,800Q240,800 240,800ZM720,880L240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,800Q800,833 776.5,856.5Q753,880 720,880ZM240,800L240,800L240,160Q240,160 240,160Q240,160 240,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM240,640L240,560L240,560L240,640Z"/>
|
||||
|
||||
</vector>
|
8
app/src/main/res/drawable/search_border.xml
Normal file
8
app/src/main/res/drawable/search_border.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@android:color/black" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
7
app/src/main/res/drawable/select_border.xml
Normal file
7
app/src/main/res/drawable/select_border.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white"/> <!-- 按钮背景色 -->
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/white"/>
|
||||
<corners android:radius="30dp"/>
|
||||
</shape>
|
12
app/src/main/res/drawable/type_audio.xml
Normal file
12
app/src/main/res/drawable/type_audio.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="32dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="32dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M430,760Q468,760 494,734Q520,708 520,670L520,520L640,520L640,440L480,440L480,595Q469,587 456.5,583.5Q444,580 430,580Q392,580 366,606Q340,632 340,670Q340,708 366,734Q392,760 430,760ZM240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L520,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,360L520,360ZM240,160L240,160L240,360L240,360L240,160L240,360L240,360L240,800Q240,800 240,800Q240,800 240,800L240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160Z" />
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/type_directory.xml
Normal file
12
app/src/main/res/drawable/type_directory.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="32dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="32dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L400,160L480,240L800,240Q833,240 856.5,263.5Q880,287 880,320L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,320Q800,320 800,320Q800,320 800,320L447,320L367,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720ZM160,720Q160,720 160,720Q160,720 160,720L160,240Q160,240 160,240Q160,240 160,240L160,240L160,320L160,320Q160,320 160,320Q160,320 160,320L160,720Q160,720 160,720Q160,720 160,720Z" />
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/type_file.xml
Normal file
12
app/src/main/res/drawable/type_file.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="32dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="32dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L520,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,360L520,360Z" />
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/type_image.xml
Normal file
12
app/src/main/res/drawable/type_image.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="32dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="32dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM240,680L720,680L570,480L450,640L360,520L240,680ZM200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760Z" />
|
||||
|
||||
</vector>
|
12
app/src/main/res/drawable/type_video.xml
Normal file
12
app/src/main/res/drawable/type_video.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="32dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="32dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M360,720L520,720Q537,720 548.5,708.5Q560,697 560,680L560,640L640,682L640,518L560,560L560,520Q560,503 548.5,491.5Q537,480 520,480L360,480Q343,480 331.5,491.5Q320,503 320,520L320,680Q320,697 331.5,708.5Q343,720 360,720ZM240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L520,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,360L520,360ZM240,160L240,160L240,360L240,360L240,160L240,360L240,360L240,800Q240,800 240,800Q240,800 240,800L240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160Z" />
|
||||
|
||||
</vector>
|
17
app/src/main/res/layout/document_card_item.xml
Normal file
17
app/src/main/res/layout/document_card_item.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/iconButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="80sp"
|
||||
android:background="@drawable/bg_roundcorner"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="30sp"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
app:drawableStartCompat="@drawable/ic_document"
|
||||
android:paddingHorizontal="20dp"
|
||||
/>
|
101
app/src/main/res/layout/document_page.xml
Normal file
101
app/src/main/res/layout/document_page.xml
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:id="@+id/main">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/DocumentPageTopBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingStart="5dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/leftArrowImageView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="left_arrow"
|
||||
android:src="@drawable/ic_left_arrow" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleDescription"
|
||||
app:layout_constraintStart_toEndOf="@+id/leftArrowImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/document"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sortDocumentView"
|
||||
app:layout_constraintStart_toEndOf="@+id/titleDescription"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="0dp"
|
||||
android:contentDescription="arrow_drop_down"
|
||||
android:src="@drawable/ic_arrow_drop_down" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/refreshData"
|
||||
app:layout_constraintEnd_toStartOf="@+id/searchDocumentView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:contentDescription="refresh"
|
||||
android:src="@drawable/baseline_refresh_24" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchDocumentView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="search"
|
||||
android:src="@drawable/ic_search"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<GridView
|
||||
android:id="@+id/DocumentGrid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="15dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/DocumentPageTopBar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:horizontalSpacing="8dp"
|
||||
android:verticalSpacing="10dp"
|
||||
android:numColumns="1"
|
||||
android:scrollbars="vertical"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbarSize="0dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/LoadingBlankText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:text="@string/loading"
|
||||
android:textSize="78sp"
|
||||
android:background="@color/WhiteSmoke"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
29
app/src/main/res/layout/document_page_search.xml
Normal file
29
app/src/main/res/layout/document_page_search.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/leftArrowImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="left_arrow"
|
||||
android:layout_marginTop="45dp"
|
||||
android:src="@drawable/ic_left_arrow" />
|
||||
|
||||
<SearchView
|
||||
android:id="@+id/searchDocument"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:background="@drawable/search_border"
|
||||
android:iconifiedByDefault="false"
|
||||
android:padding="10dp"
|
||||
android:queryHint="搜索文件"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
</LinearLayout>
|
347
app/src/main/res/layout/main_page.xml
Normal file
347
app/src/main/res/layout/main_page.xml
Normal file
|
@ -0,0 +1,347 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--本页面为主页面-->
|
||||
<!--该页面顶部的搜索文件的功能没有实现,如果没有搜索到,就显示没有文件,已经提供相关的图标:@drawable/ic_no_document-->
|
||||
<!--图片的具体情况见picture_page.xml-->
|
||||
<!--视频的具体情况见video_page.xml-->
|
||||
<!--音乐的具体情况见music_page.xml-->
|
||||
<!--文件的具体情况见document_page.xml-->
|
||||
<!--应用的具体情况见app_page.xml-->
|
||||
<!--内部存储的具体情况见store_page.xml-->
|
||||
<!--最近删除的具体情况见delete_page.xml-->
|
||||
<!--下载与接收的具体情况见download_page.xml-->
|
||||
<!--浏览器的具体情况见internet_page.xml-->
|
||||
<!--录音机的具体情况见radio_page.xml-->
|
||||
<!--安装包的具体情况见install_package_page.xml-->
|
||||
<!--压缩包的具体情况见store_package_page.xml-->
|
||||
<!--通话与信息的具体情况见phone_information_page.xml-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/WhiteSmoke"
|
||||
>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<!--设置”浏览“-->
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/MainPageTypeSelector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/searchDocument"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp"
|
||||
>
|
||||
|
||||
<!--设置“图片”按钮-->
|
||||
<Button
|
||||
android:id="@+id/MainPagePictureButton"
|
||||
android:layout_width="101dp"
|
||||
android:layout_height="108dp"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:backgroundTint="@color/design_default_color_background"
|
||||
android:drawableTop="@drawable/ic_photo"
|
||||
android:gravity="center"
|
||||
android:text="@string/picture"
|
||||
android:textSize="11sp"
|
||||
android:textColor="@color/black"
|
||||
/>
|
||||
|
||||
<!--设置“视频”按钮-->
|
||||
<Button
|
||||
android:id="@+id/MainPageMusicButton"
|
||||
android:layout_width="101dp"
|
||||
android:layout_height="108dp"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:backgroundTint="@color/design_default_color_background"
|
||||
android:drawableTop="@drawable/ic_music"
|
||||
android:gravity="center"
|
||||
android:text="@string/music"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="11sp"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/MainPageVideoButton"
|
||||
android:layout_width="101dp"
|
||||
android:layout_height="108dp"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:backgroundTint="@color/design_default_color_background"
|
||||
android:drawableTop="@drawable/ic_video"
|
||||
android:gravity="center"
|
||||
android:text="@string/video"
|
||||
android:textSize="11sp"
|
||||
android:textColor="@color/black"
|
||||
/>
|
||||
|
||||
<!--设置“音乐”按钮-->
|
||||
|
||||
<!--设置“文档”按钮-->
|
||||
<Button
|
||||
android:id="@+id/MainPageDocumentButton"
|
||||
android:layout_width="101dp"
|
||||
android:layout_height="108dp"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:backgroundTint="@color/design_default_color_background"
|
||||
android:drawableTop="@drawable/ic_document"
|
||||
android:gravity="center"
|
||||
android:text="@string/document"
|
||||
android:textSize="11sp"
|
||||
android:textColor="@color/black"
|
||||
/>
|
||||
|
||||
<!-- <!–设置“应用”按钮–>-->
|
||||
<!-- <Button-->
|
||||
<!-- android:id="@+id/MainPageAppButton"-->
|
||||
<!-- android:layout_width="101dp"-->
|
||||
<!-- android:layout_height="108dp"-->
|
||||
<!-- android:layout_gravity="start"-->
|
||||
<!-- android:layout_marginEnd="5dp"-->
|
||||
<!-- android:layout_marginStart="5dp"-->
|
||||
<!-- android:backgroundTint="@color/design_default_color_background"-->
|
||||
<!-- android:drawableTop="@drawable/ic_app"-->
|
||||
<!-- android:gravity="center"-->
|
||||
<!-- android:text="@string/app"-->
|
||||
<!-- android:textSize="11sp"-->
|
||||
<!-- android:textColor="@color/black"-->
|
||||
<!-- />-->
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<!--设置搜索框-->
|
||||
<LinearLayout
|
||||
android:id="@+id/MainPageTitleRow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/search"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="35sp"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
<!-- <!–设置”编辑“–>-->
|
||||
<!-- <Button-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginStart="200dp"-->
|
||||
<!-- android:backgroundTint="@color/design_default_color_background"-->
|
||||
<!-- android:text="@string/edit"-->
|
||||
<!-- android:textColor="@color/black"-->
|
||||
<!-- android:textSize="20sp"-->
|
||||
<!-- android:textStyle="bold" />-->
|
||||
</LinearLayout>
|
||||
|
||||
<!--设置水平滚动-->
|
||||
<LinearLayout
|
||||
android:id="@+id/MainPageLocationRow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/MainPageTypeSelector"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/advanced"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/MainPageAllFilesButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/baseline_storage_24"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/all_files"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<!--设置“内部存储”按钮-->
|
||||
<Button
|
||||
android:id="@+id/MainPageStorageButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/ic_storage"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/storage_info"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/MainPageSettingsButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/baseline_settings_24"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/settings"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<!--设置“来源”的文本-->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/source"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!--设置垂直滚动视图-->
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/WhiteSmoke"
|
||||
android:scrollbars="none"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/MainPageLocationRow"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:fillViewport="true"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<!--设置“下载与接收”按钮-->
|
||||
<Button
|
||||
android:id="@+id/MainPageDownloadButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/ic_download"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/download"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<!--设置“录音机”按钮-->
|
||||
<Button
|
||||
android:id="@+id/MainPageRecordingButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/ic_radio"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/radio"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/MainPageDCIMButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/baseline_camera_alt_24"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/camera"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/MainPagePicturesButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/baseline_photo_library_24"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/pictures"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/MainPageDocumentsButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:drawableLeft="@drawable/rounded_lab_profile_24"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="40dp"
|
||||
android:paddingStart="40dp"
|
||||
android:text="@string/documents"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
<SearchView
|
||||
android:id="@+id/searchDocument"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="15dp"
|
||||
android:background="@drawable/search_border"
|
||||
android:iconifiedByDefault="false"
|
||||
android:queryHint="搜索文件"
|
||||
android:textColor="@color/black"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/MainPageTitleRow"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
17
app/src/main/res/layout/music_card_item.xml
Normal file
17
app/src/main/res/layout/music_card_item.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/iconButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="60sp"
|
||||
android:background="@drawable/bg_roundcorner"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="30sp"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
app:drawableStartCompat="@drawable/music_icon"
|
||||
android:paddingHorizontal="20dp"
|
||||
/>
|
101
app/src/main/res/layout/music_page.xml
Normal file
101
app/src/main/res/layout/music_page.xml
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:id="@+id/main">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/MusicPageTopBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingStart="5dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/leftArrowImageView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="left_arrow"
|
||||
android:src="@drawable/ic_left_arrow" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleDescription"
|
||||
app:layout_constraintStart_toEndOf="@+id/leftArrowImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/music"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sortMusicView"
|
||||
app:layout_constraintStart_toEndOf="@+id/titleDescription"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="0dp"
|
||||
android:contentDescription="arrow_drop_down"
|
||||
android:src="@drawable/ic_arrow_drop_down" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/refreshData"
|
||||
app:layout_constraintEnd_toStartOf="@+id/searchMusicView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:contentDescription="refresh"
|
||||
android:src="@drawable/baseline_refresh_24" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchMusicView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="search"
|
||||
android:src="@drawable/ic_search"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<GridView
|
||||
android:id="@+id/MusicGrid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="15dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/MusicPageTopBar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:horizontalSpacing="8dp"
|
||||
android:verticalSpacing="10dp"
|
||||
android:numColumns="1"
|
||||
android:scrollbars="vertical"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbarSize="0dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/LoadingBlankText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:text="@string/loading"
|
||||
android:textSize="78sp"
|
||||
android:background="@color/WhiteSmoke"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
29
app/src/main/res/layout/music_page_search.xml
Normal file
29
app/src/main/res/layout/music_page_search.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/leftArrowImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="left_arrow"
|
||||
android:layout_marginTop="45dp"
|
||||
android:src="@drawable/ic_left_arrow" />
|
||||
|
||||
<SearchView
|
||||
android:id="@+id/searchMusic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:background="@drawable/search_border"
|
||||
android:iconifiedByDefault="false"
|
||||
android:padding="10dp"
|
||||
android:queryHint="搜索音乐"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
</LinearLayout>
|
25
app/src/main/res/layout/picture_card_item.xml
Normal file
25
app/src/main/res/layout/picture_card_item.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
<ImageView
|
||||
android:id="@+id/pictureCardImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/pictureCardText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:lines="2"
|
||||
android:ellipsize="end"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
111
app/src/main/res/layout/picture_page.xml
Normal file
111
app/src/main/res/layout/picture_page.xml
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--本页面为图片页面-->
|
||||
<!--长按图片,只是提供了复制,删除,剪切,粘贴的按键,并未实现功能,按键的设计在menu下的function.xml文件中,相关的java文件见picture_page.java。且只是固定提供了9张图片,不能动态增加图片,所以粘贴功能没有实现-->
|
||||
<!--要能同时删除多个图片-->
|
||||
<!--点击下箭头,是“以时间排序(默认)”和“以大小排序”也没有实现功能,具体设计在picture_page.java的showSortOption函数里面-->
|
||||
<!--点击搜索图标,是搜索图片功能,未实现搜索图片的功能,具体设计在picture_page_search的xml和java文件中,如果没有搜索到,就显示没有文件,已经提供相关的图标:@drawable/ic_no_document-->
|
||||
<!--单击图片能够显示图片,但不能左右滑动到下一张图片,要按exc键才能回到缩略图,而不是点击图片的任意位置就能回到缩略图,且单击之后不能放大缩小图片-->
|
||||
<!--picture_page.xml是主页面点击“音乐”之后进入的页面-->
|
||||
<!--picture_page_search.xml是picture_page.xml页面右上角的搜索页面-->
|
||||
<!--picture_full.xml是在picture_page.xml页面点击某张图片的缩略图后放大图片的页面-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:id="@+id/main">
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/PicturePageTopBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingStart="5dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/leftArrowImageView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="left_arrow"
|
||||
android:src="@drawable/ic_left_arrow" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleDescription"
|
||||
app:layout_constraintStart_toEndOf="@+id/leftArrowImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/picture"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sortImageView"
|
||||
app:layout_constraintStart_toEndOf="@+id/titleDescription"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="0dp"
|
||||
android:contentDescription="arrow_drop_down"
|
||||
android:src="@drawable/ic_arrow_drop_down" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/refreshData"
|
||||
app:layout_constraintEnd_toStartOf="@+id/searchImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:contentDescription="refresh"
|
||||
android:src="@drawable/baseline_refresh_24" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/searchImageView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="search"
|
||||
android:src="@drawable/ic_search"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<GridView
|
||||
android:id="@+id/PicturePageGrid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/PicturePageTopBar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:horizontalSpacing="8dp"
|
||||
android:verticalSpacing="10dp"
|
||||
android:numColumns="3"
|
||||
android:scrollbars="vertical"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbarSize="0dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/LoadingBlankText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:text="@string/loading"
|
||||
android:textSize="78sp"
|
||||
android:background="@color/WhiteSmoke"/>
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue