本文主要是受了https://github.com/siren-ocean/DocumentsUI 的启发。由于DocumentsUI依赖较少,所有拿来入门练手。
运行环境:
Android Studio Jellyfish | 2023.3.1
Build #AI-233.14808.21.2331.11709847, built on April 13, 2024
Runtime version: 17.0.10+0-17.0.10b1087.21-11572160 amd64
gradle plugin version:8.4.1
gradle version:8.6
gradle JDK:17.0.10
最终完整版可运行代码会在整理好后上传github。
一开始我是想使用低版本gradle,但是由于framework.jar下的android/content/Context.class 使用的是JDK17编译的,会导致编译报错:类文件具有错误的版本 61.0, 应为 52.0。所以就使用的JDK17(后面发现其实还是可以使用低版本的gradle和android studio 去引用高版本JDK的framework.jar)。
以下为过程简单记录。
1.新建工程,package命名为com.android.documentsui。
2.删除默认activity和res,将aosp 中packages/apps/DocumentsUI/src中的源码,res和manifestcopy到此工程。
3.minSdk修改为26,解决编译报错问题
4.添加依赖库
diff --git a/app/build.gradle b/app/build.gradle
--- a/app/build.gradle (revision 1524766154bf21ffcb0738b7901e8d21a299013d)
+++ b/app/build.gradle (revision 60b5398ef261ade6a1160d542027d9c8c7317e7b)
@@ -27,8 +27,24 @@
targetCompatibility JavaVersion.VERSION_1_8
}
}
-
+configurations.all {
+ resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+ if (details.requested.group == 'com.google.guava' && details.requested.name == 'listenablefuture') {
+ details.useTarget 'com.google.guava:guava:31.0.1-jre'
+ }
+ }
+}
dependencies {
+ compileOnly files('libs/framework.jar')
+
+ implementation libs.legacy.support.v4
+ implementation libs.commons.compress
+ // 添加 guava 依赖并排除 listenablefuture
+ implementation ('com.google.guava:guava:31.0.1-jre') {
+ exclude group: 'com.google.guava', module: 'listenablefuture'
+ }
+ implementation libs.recyclerview
+ implementation libs.recyclerview.selection
implementation libs.appcompat
implementation libs.material
@@ -37,4 +53,30 @@
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
-}
\ No newline at end of file
+}
+
+
+gradle.projectsEvaluated {
+ tasks.withType(JavaCompile) {
+ options.compilerArgs.add("-Xbootclasspath/p:$rootProject.rootDir/app/libs/framework.jar")
+ }
+}
+
+preBuild {
+ doLast {
+ // 注意:iml的路径要根据自己的实际情况来写
+ def imlFile = file("../.idea/modules/app/DocumentsUI.app.main.iml")
+ try {
+ def parsedXml = (new XmlParser()).parse(imlFile)
+ def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
+ parsedXml.component[1].remove(jdkNode)
+ def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
+ new groovy.util.Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
+ groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
+ } catch (FileNotFoundException e) {
+
+ }
+ }
+
+}
+
Index: gradle/libs.versions.toml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
--- a/gradle/libs.versions.toml (revision 1524766154bf21ffcb0738b7901e8d21a299013d)
+++ b/gradle/libs.versions.toml (revision 60b5398ef261ade6a1160d542027d9c8c7317e7b)
@@ -7,6 +7,10 @@
material = "1.12.0"
activity = "1.9.0"
constraintlayout = "2.1.4"
+recyclerview = "1.2.1"
+recyclerviewSelection = "1.1.0"
+legacySupportV4 = "1.0.0"
+commonsCompress = "1.26.2"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -16,6 +20,10 @@
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
+recyclerview-selection = { group = "androidx.recyclerview", name = "recyclerview-selection", version.ref = "recyclerviewSelection" }
+legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupportV4" }
+commons-compress = { group = "org.apache.commons", name = "commons-compress", version.ref = "commonsCompress" }
5.解決编译报错:元素值必须为常量表达式
diff --git a/gradle.properties b/gradle.properties
--- a/gradle.properties (revision 60b5398ef261ade6a1160d542027d9c8c7317e7b)
+++ b/gradle.properties (revision bbea8c3f04c21ac89801d301f162f787a1002c91)
@@ -18,4 +18,6 @@
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+
+android.nonFinalResIds=false
6.把缺少的java/com/android/modules/utils/build 下的SdkLevel和UnboundedSdkLevel放入android studio工程。
7.解决缺少id问题。
部分id存在于
out/soong/.intermediates/packages/apps/DocumentsUI/DocumentsUI/android_common/R.txt 这个文件中。可以在android studio中新建ids.xml 文件。
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
--- /dev/null (revision e64969f8df18fe4f2a8200c70a1a7e5ab1ce153c)
+++ b/app/src/main/res/values/ids.xml (revision e64969f8df18fe4f2a8200c70a1a7e5ab1ce153c)
@@ -0,0 +1,20 @@
+<resources>
+ <item type="id" name="search_src_text" />
+ <item type="id" name="search_close_btn" />
+ <item type="id" name="action_mode_bar" />
+ <item type="id" name="option_menu_show_hidden_files" />
+ <item type="id" name="sub_menu_grid" />
+ <item type="id" name="sub_menu_list" />
+ <item type="id" name="dir_menu_create_dir" />
+
+ <item type="id" name="option_menu_create_dir" />
+ <item type="id" name="option_menu_search" />
+ <item type="id" name="option_menu_select_all" />
+ <item type="id" name="option_menu_debug" />
+
+ <item type="id" name="option_menu_sort" />
+
+ <item type="id" name="action_mode_close_button" />
+
+
+</resources>
\ No newline at end of file
8.添加DocumentsStatsLog文件
DocumentsStatsLog位于out/soong/.intermediates/packages/apps/DocumentsUI/statslog-docsui-java-gen/gen/com/android/documentsui/DocumentsStatsLog.java,把此文件copy至app/src/main/java/com/android/documentsui/DocumentsStatsLog.java。
9.解决其他编译语法报错
diff --git a/app/src/main/java/com/android/documentsui/DirectoryLoader.java b/app/src/main/java/com/android/documentsui/DirectoryLoader.java
--- a/app/src/main/java/com/android/documentsui/DirectoryLoader.java (revision 0e3dfb4862f2129032c0d387b3e089e3426e418d)
+++ b/app/src/main/java/com/android/documentsui/DirectoryLoader.java (revision d3a6a73d79a22f891e1f9a263687a2296b29afad)
@@ -100,7 +100,7 @@
mPhotoPicking = state.isPhotoPicking();
}
- @Override
+ //@Override
protected Executor getExecutor() {
return ProviderExecutor.forAuthority(mRoot.authority);
}
Index: app/src/main/java/com/android/documentsui/archives/WriteableArchive.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/app/src/main/java/com/android/documentsui/archives/WriteableArchive.java b/app/src/main/java/com/android/documentsui/archives/WriteableArchive.java
--- a/app/src/main/java/com/android/documentsui/archives/WriteableArchive.java (revision 0e3dfb4862f2129032c0d387b3e089e3426e418d)
+++ b/app/src/main/java/com/android/documentsui/archives/WriteableArchive.java (revision d3a6a73d79a22f891e1f9a263687a2296b29afad)
@@ -298,7 +298,7 @@
synchronized (mEntries) {
for (final String path : mPendingEntries) {
try {
- mZipOutputStream.putArchiveEntry(mEntries.get(path));
+ mZipOutputStream.putArchiveEntry((ZipArchiveEntry)mEntries.get(path));
mZipOutputStream.closeArchiveEntry();
} catch (IOException e) {
Log.e(TAG, "Failed to flush empty entries.", e);
Index: app/src/main/java/com/android/documentsui/inspector/KeyValueRow.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/app/src/main/java/com/android/documentsui/inspector/KeyValueRow.java b/app/src/main/java/com/android/documentsui/inspector/KeyValueRow.java
--- a/app/src/main/java/com/android/documentsui/inspector/KeyValueRow.java (revision 0e3dfb4862f2129032c0d387b3e089e3426e418d)
+++ b/app/src/main/java/com/android/documentsui/inspector/KeyValueRow.java (revision d3a6a73d79a22f891e1f9a263687a2296b29afad)
@@ -98,8 +98,8 @@
public void setOnClickListener(OnClickListener callback) {
TextView clickable = ((TextView) findViewById(R.id.table_row_value));
mDefaultTextColor = clickable.getTextColors();
- TypedArray ta = getContext().obtainStyledAttributes(R.styleable.TextAppearance);
- int linkColor = ta.getColor(R.styleable.TextAppearance_android_textColorLink,
+ TypedArray ta = getContext().obtainStyledAttributes(android.R.styleable.TextAppearance);
+ int linkColor = ta.getColor(android.R.styleable.TextAppearance_textColorLink,
mDefaultTextColor.getDefaultColor());
ta.recycle();
clickable.setTextColor(linkColor);
10.使用系统签名
这里可以直接使用https://github.com/siren-ocean/DocumentsUI
这里的文件
11.修改versionCode,改成35.
12.解决运行时错误,注释部分代码
diff --git a/app/src/main/java/com/android/documentsui/roots/ProvidersCache.java b/app/src/main/java/com/android/documentsui/roots/ProvidersCache.java
--- a/app/src/main/java/com/android/documentsui/roots/ProvidersCache.java (revision 900dbfa600a2c680e08016ba0371ba545c0d7e17)
+++ b/app/src/main/java/com/android/documentsui/roots/ProvidersCache.java (revision a390c75512f2515fa2a7410efcef9ce80dfe36f6)
@@ -207,7 +207,7 @@
assert (recentRoot.rootId == null);
assert (recentRoot.derivedIcon == R.drawable.ic_root_recent);
assert (recentRoot.derivedType == RootInfo.TYPE_RECENTS);
- assert (recentRoot.flags == (Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD));
+ // assert (recentRoot.flags == (Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD));
assert (recentRoot.availableBytes == -1);
}