I'm new to android and I'm trying to develop file explorer which includes search function. I'm using a recursive search function that works fine in folders with a few subfolders and files, but for some reason it's EXTREMELY SLOW and could "Force Close" in folders with lots of subfolders and files, because there's not enough memory. I do the search by creating ArrayList where the results will be placed, and then calling the recursive function that will fill the list. The "path" argument is the file where the search will start from, and "query" is the search query.
ArrayList<File> result = new ArrayList<File>();
fileSearch(path, query, result);
this is what the recursive function looks like:
private void fileSearch(File dir, String query, ArrayList<File> res) {
if (dir.getName().toLowerCase().contains(query.toLowerCase()))
res.add(dir);
if (dir.isDirectory() && !dir.isHidden()) {
if (dir.list() != null) {
for (File item : dir.listFiles()) {
fileSearch(item, query, res);
}
}
}
}
If someone could point me to a way of performing a faster and/or more efficient file search, I would really appreciate that.
EDIT:
This is how I tried to do the job with AsyncTask:
private class Search extends AsyncTask<File, Integer, Void> {
String query;
ArrayList<File> result = new ArrayList<File>();
public Search(String query){
this.query = query;
setTitle("Searching");
}
#Override
protected Void doInBackground(File... item) {
int count = item.length;
for (int i = 0; i < count; i++) {
fileSearch(item[i], query, result);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return null;
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress[0]);
}
protected void onPostExecute() {
searchResults = new ListItemDetails[result.size()];
for (int i = 0; i < result.size(); i++) {
File temp = result.get(i);
if (temp.isDirectory())
searchResults[i] = new ListItemDetails(temp.getAbsolutePath(),
R.drawable.folder, temp.lastModified(), temp.length());
else {
String ext;
if (temp.getName().lastIndexOf('.') == -1)
ext = "";
else
ext = temp.getName().substring(
temp.getName().lastIndexOf('.'));
searchResults[i] = new ListItemDetails(temp.getAbsolutePath(),
getIcon(ext), temp.lastModified(), temp.length());
}
}
finishSearch();
}
}
public void finishSearch() {
Intent intent = new Intent(this, SearchResults.class);
startActivity(intent);
}
The call to finishSearch() is just so I can create the Intent to show the results in other Activity. Any ideas, suggestions, tips? Thanks in advance
It is possible that you are hitting symbolic links and going into an infinitive loop with your search function and depleting available memory to your application.
I would suggest you to keep a separate list containing canonical paths (File.getCanonicalPath()) of directories you've visited and avoid visiting them over and over again.
Why don't you use Apache Commons IO? It has some functions to deal with searching.
I also suggest using the method FileUtils.listFiles, which takes a folder, a search query and a directory filter as parameters.
The following example returns you a list of all file's paths that matched according to a regex. Try adding it in doInBackground of your AsyncTask:
Collection files = FileUtils.listFiles(new File(yourRootPath),
new RegexFileFilter(searchQuery),
DirectoryFileFilter.DIRECTORY);
Have you looked into Lucene?
It is especially designed to index and query large numbers of free-text documents, so many of the I/O streaming and indexing tasks have already been solved for you. If you remove the recursion and do the document indexing using a Lucene index in a purely iterative fashion, memory issues may be mitigated.
Look into this thread:
Lucene in Android
Do it in the background, and starting from Android O (API 26) , you can use Files.find API. Exmaple:
Files.find(
Paths.get(startPath), Integer.MAX_VALUE,
{ path, _ -> path.fileName.toString() == file.name }
).forEach { foundPath ->
Log.d("AppLog", "found file on:${foundPath.toFile().absolutePath}")
}
Related
I want to initialize MediaPlayer instances for all of the soundfiles found in res/raw:
/res/raw/test1.mp3
/res/raw/test2.mp3
/res/raw/testN.mp3
Purpose is to play different samples on a button click, without delays.
List<MediaPlayer> player = new ArrayList<>();
//TODO how to loop properly?
for (Rawfile file : rawfiles) {
pl = MediaPlayer.create(getBaseContext(), R.raw.test1);
player.add(pl);
}
Lateron, if eg button2 is clicked:
player.get(1).start();
Question: how can I get the R.raw.* files dynamically during initialization of the app?
Update: the following is quite close, but there are 2 problems:
1) If eg only one file "test.mp3" is placed in my /res/raw folder, the function shows 3 files.
2) How can I then load those files to mediaplayer?
public void listRaw(){
Field[] fields=R.raw.class.getFields();
for(int count=0; count < fields.length; count++){
Log.i("Raw Asset: ", fields[count].getName());
}
}
Result:
I/Raw Asset:: $change
I/Raw Asset:: serialVersionUID
I/Raw Asset:: test
For the moment solved as follows, but feels kinda hacky:
public static List<Integer> listRawMediaFiles() {
List<Integer> ids = new ArrayList<>();
for (Field field : R.raw.class.getFields()) {
try {
ids.add(field.getInt(field));
} catch (Exception e) {
//compiled app contains files like '$change' or 'serialVersionUID'
//which are no real media files
}
}
return ids;
}
I Really forget from where i get this, it can be duplicated any way not my code but works perfectly :
private boolean listFiles(String path) {
String [] list;
try {
list = getAssets().list(path);
if (list.length > 0) {
// folder founded
for (String file : list) {
if (!listAssetFiles(path + "/" + file))
return false;
}
} else {
//file founded
}
} catch (IOException e) {
return false;
}
return true;
}
Though undervoted, this answer from CommonsWare is comprehensive. The best you can do is iterate over the raw fields by reflection. If you find non-resource fields, you should discard them manually (I see you did it in an answer).
One point: putting files in raw directory is done during development time, and programming the iteration over raw resources is also done during development time. It's a problem you should solve before compilation, rather than finding out what files you have in run time, that is, you should list the files by name in your code.
I am trying to get the app code and display it, for an example if button X starts a new activity then a textView displays the whole method
I reached only how can I display code in HTML format from this question
But is there is a way to get the code of my app out, I think that there are 2 ways
An Internal one by getting it by the app itself
An External one by reading the java file then filtering it and getting the text of the method
Is there are any ideas about that?
Thanks in advance
The above is not currently possible as mentioned by others is the comments. What i can suggest is shipping your application with the source code in the assets folder and using a helper function to extract a certain methods from the source at runtime (your second proposed approach). I have written example code but it is in pure java and needs to be ported to android (a few lines).
NB: You may need to reformat the code after extraction depending on your use case.
Hope it helps :)
The code for the helper method:
static String getTheCode(String classname ,String methodSignature ) throws FileNotFoundException {
//**********************A few lines of code below need changing when porting ***********//
// open file, your will be in the assets folder not in the home dir of user, don't forget the .java extension when porting
File file = new File(System.getProperty("user.home") +"/"+ classname +".java");
// get the source, you can use FileInputReader or some reader supported by android
Scanner scanner = new Scanner(file);
String source = "";
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
//**********************The above code needs changing when porting **********//
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0 ,lastIndex);
}
Example usage (getTheCode methods is static and in a class called GetTheCode):
public static void main(String... s) throws FileNotFoundException {
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut()"));
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut(String word)"));
}
Output:
private static void shoutOut(){ // nothing to here }
private static void shoutOut(String word){ // nothing to here }
NB: When starting your new activity create a method eg
private void myStartActivty(){
Intent intent = new Intent(MyActivity.this, AnotherActivity.class);
startActivity(intent);
}
Then in your onClick:
#Override
public void onClick(View v) {
myStartActivity();
myTextView.setText(GetTheCode.getTheCode("MyActivity","private void myStartActivity()"));
}
Update: Ported the Code for android:
import android.content.Context;
import java.io.IOException;
import java.util.Scanner;
public class GetTheCode {
static String getTheCode(Context context, String classname , String methodSignature ) {
Scanner scanner = null;
String source = "";
try {
scanner = new Scanner(context.getAssets().open(classname+".java"));
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
scanner.close();
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0,lastIndex);
}
}
Usage:
// the method now takes in context as the first parameter, the line below was in an Activity
Log.d("tag",GetTheCode.getTheCode(this,"MapsActivity","protected void onCreate(Bundle savedInstanceState)"));
Let's start with a broader overview of the problem:
Display App code
Press X button
Open new activity with a textview which displays the method
The goal is to do the following:
Viewing app method by extracting it and then building & running it.
There are some methods we can use to run Java/Android code dynamically. The way I would personally do it is DexClassLoader and with Reflection.
If you need more details, let me know. Here is what it'd do though:
View app method
Upon pressing X, launch intent with extra to new Activity
Parse and compile code dynamically and then run it with DexClassLoader and Reflection
Sources:
Sample file loading Java method from TerminalIDE Android App
Android Library I made for Auto-Updating Android Applications without needing the Play Store on non-root devices
Hello StackOverflow pals,
I'm using Office-365-SDK for Android ( https://github.com/OfficeDev/Office-365-SDK-for-Android ), I've been looking on all SDK examples (https://msdn.microsoft.com/en-us/office/office365/howto/starter-projects-and-code-samples) and also I've been looking into SDK source code directly but I'm not able to figure how to list files under a folder; all examples does only file list under root folder.
On Office365 REST API I can see clearly that there's a call for this pourpose ( https://msdn.microsoft.com/office/office365/APi/files-rest-operations#FolderoperationsListfoldercontentsREST ) but on this SDK I didn't found a way to create the same call.
My actual code is the very same than the one on SDK code snippets ( https://github.com/OfficeDev/O365-Android-Snippets/blob/master/app/src/main/java/com/microsoft/office365/snippetapp/Snippets/FileFolderSnippets.java ) on call "getFilesAndFolders". It list properly files and folder under root, but I haven't a way to list files and folder under a concrete folder, so I cannot create a file explorer :(.
Thanks in advance! Regards.
I've found a way to achieve this, isn't an elegant one but maybe can help to someone, take a look into this code example:
public ChildsSharePointClient getFileClient(String appendPath) {
if (mServices == null || mServices.isEmpty()) {
mCountDownLatch = new CountDownLatch(1);
discoverServices();
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (mFileServiceInfo == null) {
mFileServiceInfo = getService(Constants.MYFILES_CAPABILITY);
}
String serviceEndpointUri = mFileServiceInfo.getserviceEndpointUri();
String serviceResourceId = mFileServiceInfo.getserviceResourceId();
if (!TextUtils.isEmpty(appendPath)) {
serviceEndpointUri += appendPath;
}
AuthenticationManager.getInstance().setResourceId(serviceResourceId);
DefaultDependencyResolver dependencyResolver = (DefaultDependencyResolver) AuthenticationManager.getInstance()
.getDependencyResolver();
return new ChildsSharePointClient(serviceEndpointUri, dependencyResolver);
}
public List<Item> getFilesList(String folderId) {
List<Item> filesAndFolders = null;
try {
if (TextUtils.isEmpty(folderId)) {
folderId = ROOT_PATH;
}
filesAndFolders = getFileClient(FILES_PATH + "/" + folderId)
.getChilds()
.read()
.get();
LOG.debug("Retrieved {} elements",filesAndFolders.size());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return filesAndFolders;
}
/**
* Created by mike on 16/06/15.
*/
public class ChildsSharePointClient extends SharePointClient {
/**
* Instantiates a new SharePointClient.
*
* #param url the url
* #param resolver the resolver
*/
public ChildsSharePointClient(String url, DependencyResolver resolver) {
super(url, resolver);
}
/**
* Gets Item.
*
* #return the Item
*/
public ODataCollectionFetcher<Item, ItemFetcher, ItemCollectionOperations> getChilds() {
return new ODataCollectionFetcher<Item, ItemFetcher,ItemCollectionOperations>("children", this, Item.class,ItemCollectionOperations.class);
}
}
Basically I'm initiating SharePointClient directly with url with id of desired folder and I've added a getChild() action into a class that inherit from MS SharePointClient and request for "children" item.
Hope it helps to someone in the meanwhile we found a more elegant solution.
Proper way to do it:
//retrieve the folder (as item)
Item item = client.getFiles().getOperations().getByPath("foo/path").get();
//get the folder, and get the children
client.getFiles().getById(item.getId()).asFolder().getChildren().read();
Marco Torres from Microsoft have answered this question on one ticket that I've opened on SDK github -> https://github.com/OfficeDev/Office-365-SDK-for-Android/issues/88
Hope it helps to someone :)
I am trying to read from a file, and I want to use Bzip2InputStream to do it. When i try to run my application it runs fine untill the code reaches the creation of the inputstream. At wich point it keeps running, but nothing happens and roughly every second the application output reads:
[Mono] GC_MINOR: (Nursery full) pause 5.67ms, total 6.16ms, bridge 0.00ms promoted 192K major 1152K los 1075K
Is there an alternatives I do not know about? I am doing something stupid? Thanks for any help!
The code is as follows.
using Ionic.BZip2;
namespace ActionBarTest
{
public class CoverageElement
{
}
public class Coverages
{
private List<string> ChartNames = new List<string> ();
private List<CoverageElement> m_Coverages;
private BZip2InputStream bz2Stream;
public Coverages(Stream compressedCoverage)
{
try{
bz2Stream = new Ionic.BZip2.BZip2InputStream(compressedCoverage);
}
catch (Exception error){
}
using (BinaryReader b = new BinaryReader (bz2Stream)) {
int t1 = b.ReadInt32 ();
Console.WriteLine ("krøll i binaryreader" + t1);
int noOfElements = b.ReadInt32 ();
for (int i = 0; i < noOfElements; i++) {
char[] t = b.ReadChars (8);
ChartNames.Add (new string (t));
}
}
}
}
}
I found out the answer, instead of sending the stream directly from the webclient result I needed to make a memorystream from the webclient result, and use that.
You need to make a memorystream from the webclient result and use that. You can't just send the stream directly from the webclient result. Memorystreams always help, and it makes everything easier to read, and sometimes it's the key to doability altogether.
Is there a Java equivalent for System.IO.Path.Combine() in C#/.NET? Or any code to accomplish this?
This static method combines one or more strings into a path.
Rather than keeping everything string-based, you should use a class which is designed to represent a file system path.
If you're using Java 7 or Java 8, you should strongly consider using java.nio.file.Path; Path.resolve can be used to combine one path with another, or with a string. The Paths helper class is useful too. For example:
Path path = Paths.get("foo", "bar", "baz.txt");
If you need to cater for pre-Java-7 environments, you can use java.io.File, like this:
File baseDirectory = new File("foo");
File subDirectory = new File(baseDirectory, "bar");
File fileInDirectory = new File(subDirectory, "baz.txt");
If you want it back as a string later, you can call getPath(). Indeed, if you really wanted to mimic Path.Combine, you could just write something like:
public static String combine(String path1, String path2)
{
File file1 = new File(path1);
File file2 = new File(file1, path2);
return file2.getPath();
}
In Java 7, you should use resolve:
Path newPath = path.resolve(childPath);
While the NIO2 Path class may seem a bit redundant to File with an unnecessarily different API, it is in fact subtly more elegant and robust.
Note that Paths.get() (as suggested by someone else) doesn't have an overload taking a Path, and doing Paths.get(path.toString(), childPath) is NOT the same thing as resolve(). From the Paths.get() docs:
Note that while this method is very convenient, using it will imply an assumed reference to the default FileSystem and limit the utility of the calling code. Hence it should not be used in library code intended for flexible reuse. A more flexible alternative is to use an existing Path instance as an anchor, such as:
Path dir = ...
Path path = dir.resolve("file");
The sister function to resolve is the excellent relativize:
Path childPath = path.relativize(newPath);
The main answer is to use File objects. However Commons IO does have a class FilenameUtils that can do this kind of thing, such as the concat() method.
platform independent approach (uses File.separator, ie will works depends on operation system where code is running:
java.nio.file.Paths.get(".", "path", "to", "file.txt")
// relative unix path: ./path/to/file.txt
// relative windows path: .\path\to\filee.txt
java.nio.file.Paths.get("/", "path", "to", "file.txt")
// absolute unix path: /path/to/filee.txt
// windows network drive path: \\path\to\file.txt
java.nio.file.Paths.get("C:", "path", "to", "file.txt")
// absolute windows path: C:\path\to\file.txt
I know its a long time since Jon's original answer, but I had a similar requirement to the OP.
By way of extending Jon's solution I came up with the following, which will take one or more path segments takes as many path segments that you can throw at it.
Usage
Path.combine("/Users/beardtwizzle/");
Path.combine("/", "Users", "beardtwizzle");
Path.combine(new String[] { "/", "Users", "beardtwizzle", "arrayUsage" });
Code here for others with a similar problem
public class Path {
public static String combine(String... paths)
{
File file = new File(paths[0]);
for (int i = 1; i < paths.length ; i++) {
file = new File(file, paths[i]);
}
return file.getPath();
}
}
To enhance JodaStephen's answer, Apache Commons IO has FilenameUtils which does this. Example (on Linux):
assert org.apache.commons.io.FilenameUtils.concat("/home/bob", "work\\stuff.log") == "/home/bob/work/stuff.log"
It's platform independent and will produce whatever separators your system needs.
Late to the party perhaps, but I wanted to share my take on this. I prefer not to pull in entire libraries for something like this. Instead, I'm using a Builder pattern and allow conveniently chained append(more) calls. It even allows mixing File and String, and can easily be extended to support Path as well. Furthermore, it automatically handles the different path separators correctly on both Linux, Macintosh, etc.
public class Files {
public static class PathBuilder {
private File file;
private PathBuilder ( File root ) {
file = root;
}
private PathBuilder ( String root ) {
file = new File(root);
}
public PathBuilder append ( File more ) {
file = new File(file, more.getPath()) );
return this;
}
public PathBuilder append ( String more ) {
file = new File(file, more);
return this;
}
public File buildFile () {
return file;
}
}
public static PathBuilder buildPath ( File root ) {
return new PathBuilder(root);
}
public static PathBuilder buildPath ( String root ) {
return new PathBuilder(root);
}
}
Example of usage:
File root = File.listRoots()[0];
String hello = "hello";
String world = "world";
String filename = "warez.lha";
File file = Files.buildPath(root).append(hello).append(world)
.append(filename).buildFile();
String absolute = file.getAbsolutePath();
The resulting absolute will contain something like:
/hello/world/warez.lha
or maybe even:
A:\hello\world\warez.lha
If you do not need more than strings, you can use com.google.common.io.Files
Files.simplifyPath("some/prefix/with//extra///slashes" + "file//name")
to get
"some/prefix/with/extra/slashes/file/name"
Here's a solution which handles multiple path parts and edge conditions:
public static String combinePaths(String ... paths)
{
if ( paths.length == 0)
{
return "";
}
File combined = new File(paths[0]);
int i = 1;
while ( i < paths.length)
{
combined = new File(combined, paths[i]);
++i;
}
return combined.getPath();
}
This also works in Java 8 :
Path file = Paths.get("Some path");
file = Paths.get(file + "Some other path");
This solution offers an interface for joining path fragments from a String[] array. It uses java.io.File.File(String parent, String child):
public static joinPaths(String[] fragments) {
String emptyPath = "";
return buildPath(emptyPath, fragments);
}
private static buildPath(String path, String[] fragments) {
if (path == null || path.isEmpty()) {
path = "";
}
if (fragments == null || fragments.length == 0) {
return "";
}
int pathCurrentSize = path.split("/").length;
int fragmentsLen = fragments.length;
if (pathCurrentSize <= fragmentsLen) {
String newPath = new File(path, fragments[pathCurrentSize - 1]).toString();
path = buildPath(newPath, fragments);
}
return path;
}
Then you can just do:
String[] fragments = {"dir", "anotherDir/", "/filename.txt"};
String path = joinPaths(fragments);
Returns:
"/dir/anotherDir/filename.txt"
Assuming all given paths are absolute paths. you can follow below snippets to merge these paths.
String baseURL = "\\\\host\\testdir\\";
String absoluteFilePath = "\\\\host\\testdir\\Test.txt";;
String mergedPath = Paths.get(baseURL, absoluteFilePath.replaceAll(Matcher.quoteReplacement(baseURL), "")).toString();
output path is \\host\testdir\Test.txt.