android: Turn 3 "internal" docs into raw resources

Make 'example.odt', 'license.txt' and 'notice.txt'
(which can be opened via the "About" dialog)
resources of the app by copying them to a new directory
'res_generated' instead of into assets, and include
'res_generated' into resources, then use an
'android.resource://' URI instead of a 'file:///assets/'
one in AboutDialogFragment.
The latter does not work with when passed as a parameter
to 'ContentResolver.openInputStream'.

Adapt/Simplify 'LibreOfficeMainActivity#copyFileToTemp' to
make loading those docs using the 'android.resource://'
URI work and use the existing 'copyStream' method for copying from
the input to the output stream.

This is in preparation for upcoming commit with Change-Id
I7731ef81a4242fa0ce3b3fd8ced1683a6a6bee8c,
"android: Always create a temporary local copy of the doc".

Change-Id: I7731ef81a4242fa0ce3b3fd8ced1683a6a6bee8c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113881
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
This commit is contained in:
Michael Weghorn
2021-04-09 10:08:19 +02:00
parent bcb74b5092
commit d3f8f4b166
4 changed files with 51 additions and 62 deletions

1
android/.gitignore vendored
View File

@ -6,6 +6,7 @@
/source/build/
/source/captures/
/source/jni/Application.mk
/source/res_generated
/jniLibs/
/source/liboSettings.gradle
/source/local.properties

View File

@ -42,7 +42,7 @@ android {
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
main.assets.srcDirs = ['assets']
main.res.srcDirs = ['res']
main.res.srcDirs = ['res', 'res_generated']
main.java.srcDirs = ['../Bootstrap/src', 'src/java']
main.jniLibs.srcDirs = ["${liboJniLibsdir}"]
main.jni.srcDirs = [] // don't attempt to build native-lib via gradle
@ -144,14 +144,6 @@ task copyUnpackAssets(type: Copy) {
task copyAssets(type: Copy) {
description "copies assets that can be accessed within the installed apk"
into 'assets'
from("${liboInstdir}") {
includes = ["LICENSE", "NOTICE"]
rename "LICENSE", "license.txt"
rename "NOTICE", "notice.txt"
}
from("${liboExampleDocument}") {
rename ".*", "example.odt"
}
// include icons
into ('share') {
@ -193,6 +185,19 @@ task copyAssets(type: Copy) {
}
}
task copyAppResources(type: Copy) {
description "copies documents to make them available as app resources"
into 'res_generated/raw'
from("${liboInstdir}") {
includes = ["LICENSE", "NOTICE"]
rename "LICENSE", "license.txt"
rename "NOTICE", "notice.txt"
}
from("${liboExampleDocument}") {
rename ".*", "example.odt"
}
}
task createFullConfig(type: Copy) {
// grab dir to clear whole hierarchy on clean target
outputs.dir "assets_fullUI"
@ -290,7 +295,8 @@ task createRCfiles {
preBuild.dependsOn 'createRCfiles',
'createStrippedConfigMain',
'createStrippedConfigRegistry',
'createFullConfig'
'createFullConfig',
'copyAppResources'
clean.dependsOn 'cleanCopyAssets',
'cleanCreateStrippedConfig',

View File

@ -30,9 +30,6 @@ import java.io.File;
public class AboutDialogFragment extends DialogFragment {
private static final String DEFAULT_DOC_PATH = "/assets/example.odt";
@NonNull @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
@ -81,21 +78,21 @@ public class AboutDialogFragment extends DialogFragment {
.setNegativeButton(R.string.about_license, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
loadFromAbout("/assets/license.txt");
loadFromAbout(R.raw.license);
dialog.dismiss();
}
})
.setPositiveButton(R.string.about_notice, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
loadFromAbout("/assets/notice.txt");
loadFromAbout(R.raw.notice);
dialog.dismiss();
}
})
.setNeutralButton(R.string.about_moreinfo, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
loadFromAbout(DEFAULT_DOC_PATH);
loadFromAbout(R.raw.example);
dialog.dismiss();
}
});
@ -103,8 +100,8 @@ public class AboutDialogFragment extends DialogFragment {
return builder.create();
}
private void loadFromAbout(String input) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.fromFile(new File(input)));
private void loadFromAbout(int resourceId) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("android.resource://" + BuildConfig.APPLICATION_ID + "/" + resourceId));
String packageName = getActivity().getApplicationContext().getPackageName();
ComponentName componentName = new ComponentName(packageName, LibreOfficeMainActivity.class.getName());
i.setComponent(componentName);

View File

@ -175,7 +175,8 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
mDocumentUri = getIntent().getData();
if (mDocumentUri != null) {
if (mDocumentUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
if (mDocumentUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)
|| mDocumentUri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE)) {
final boolean isReadOnlyDoc;
if (getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY) != null) {
// New document type string is not null, meaning we want to open a new document
@ -296,53 +297,37 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
try {
mTempFile = File.createTempFile("LibreOffice", suffix, this.getCacheDir());
final FileChannel outputChannel = new FileOutputStream(mTempFile).getChannel();
try {
// need to run copy operation in a separate thread, since network access is not
// allowed from main thread, but that may happen here when underlying
// DocumentsProvider (like the NextCloud one) does that
class CopyThread extends Thread {
/** Whether copy operation was successful. */
private boolean result = false;
final FileOutputStream outputStream = new FileOutputStream(mTempFile);
// need to run copy operation in a separate thread, since network access is not
// allowed from main thread, but that may happen here when underlying
// DocumentsProvider (like the NextCloud one) does that
class CopyThread extends Thread {
/** Whether copy operation was successful. */
private boolean result = false;
@Override
public void run() {
result = false;
try {
final AssetFileDescriptor assetFD = contentResolver.openAssetFileDescriptor(mDocumentUri, "r");
if (assetFD == null) {
Log.e(LOGTAG, "couldn't create assetfiledescriptor from " + mDocumentUri);
return;
}
FileChannel inputChannel = assetFD.createInputStream().getChannel();
long bytesTransferred = 0;
// might not copy all at once, so make sure everything gets copied...
while (bytesTransferred < inputChannel.size()) {
bytesTransferred += outputChannel.transferFrom(inputChannel, bytesTransferred, inputChannel.size());
}
Log.e(LOGTAG, "Success copying " + bytesTransferred + " bytes");
inputChannel.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
result = true;
@Override
public void run() {
result = false;
try {
InputStream inputStream = contentResolver.openInputStream(mDocumentUri);
result = copyStream(inputStream, outputStream);
} catch (IOException e) {
e.printStackTrace();
return;
}
};
CopyThread copyThread = new CopyThread();
copyThread.start();
try {
// wait for copy operation to finish
// NOTE: might be useful to add some indicator in UI for long copy operations involving network...
copyThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return copyThread.result;
} finally {
outputChannel.close();
};
CopyThread copyThread = new CopyThread();
copyThread.start();
try {
// wait for copy operation to finish
// NOTE: might be useful to add some indicator in UI for long copy operations involving network...
copyThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return copyThread.result;
} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {