summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimonx22 <simon@oatmealdome.me>2026-04-30 16:28:38 -0400
committerSimonx22 <simon@oatmealdome.me>2026-07-11 12:33:11 -0400
commitc93af1d3f94deed81cf288f73c3cfd90aae8e500 (patch)
treefca8cc54740e87bbdcc4852ded2ad17572995d35
parentaf15769d57ddaf4b3f79801a2174c67a19150050 (diff)
Android: Convert ContentHandler to Kotlin and fix warnings
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java425
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.kt331
2 files changed, 331 insertions, 425 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java
deleted file mode 100644
index 9143375f76..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java
+++ /dev/null
@@ -1,425 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.utils;
-
-import android.content.ContentResolver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.dolphinemu.dolphinemu.DolphinApplication;
-
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Predicate;
-
-/*
- We use a lot of "catch (Exception e)" in this class. This is for two reasons:
-
- 1. We don't want any exceptions to escape to native code, as this leads to nasty crashes
- that often don't have stack traces that make sense.
-
- 2. The sheer number of different exceptions, both documented and undocumented. These include:
- - FileNotFoundException when a file doesn't exist
- - FileNotFoundException when using an invalid open mode (according to the documentation)
- - IllegalArgumentException when using an invalid open mode (in practice with FileProvider)
- - IllegalArgumentException when providing a tree where a document was expected and vice versa
- - SecurityException when trying to access something the user hasn't granted us permission to
- - UnsupportedOperationException when a URI specifies a storage provider that doesn't exist
- */
-
-public class ContentHandler
-{
- public static boolean isContentUri(@NonNull String pathOrUri)
- {
- return pathOrUri.startsWith("content://");
- }
-
- @Keep
- public static int openFd(@NonNull String uri, @NonNull String mode)
- {
- try
- {
- return getContentResolver().openFileDescriptor(unmangle(uri), mode).detachFd();
- }
- catch (SecurityException e)
- {
- Log.error("Tried to open " + uri + " without permission");
- }
- catch (Exception ignored)
- {
- }
-
- return -1;
- }
-
- @Keep
- public static boolean delete(@NonNull String uri)
- {
- try
- {
- return DocumentsContract.deleteDocument(getContentResolver(), unmangle(uri));
- }
- catch (FileNotFoundException e)
- {
- // Return true because we care about the file not being there, not the actual delete.
- return true;
- }
- catch (SecurityException e)
- {
- Log.error("Tried to delete " + uri + " without permission");
- }
- catch (Exception ignored)
- {
- }
-
- return false;
- }
-
- public static boolean exists(@NonNull String uri)
- {
- try
- {
- Uri documentUri = treeToDocument(unmangle(uri));
- final String[] projection = new String[]{Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE};
- try (Cursor cursor = getContentResolver().query(documentUri, projection, null, null, null))
- {
- return cursor != null && cursor.getCount() > 0;
- }
- }
- catch (SecurityException e)
- {
- Log.error("Tried to check if " + uri + " exists without permission");
- }
- catch (Exception ignored)
- {
- }
-
- return false;
- }
-
- /**
- * @return -1 if not found, -2 if directory, file size otherwise
- */
- @Keep
- public static long getSizeAndIsDirectory(@NonNull String uri)
- {
- try
- {
- Uri documentUri = treeToDocument(unmangle(uri));
- final String[] projection = new String[]{Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE};
- try (Cursor cursor = getContentResolver().query(documentUri, projection, null, null, null))
- {
- if (cursor != null && cursor.moveToFirst())
- {
- if (Document.MIME_TYPE_DIR.equals(cursor.getString(0)))
- return -2;
- else
- return cursor.isNull(1) ? 0 : cursor.getLong(1);
- }
- }
- }
- catch (SecurityException e)
- {
- Log.error("Tried to get metadata for " + uri + " without permission");
- }
- catch (Exception ignored)
- {
- }
-
- return -1;
- }
-
- @Nullable @Keep
- public static String getDisplayName(@NonNull String uri)
- {
- try
- {
- return getDisplayName(unmangle(uri));
- }
- catch (Exception ignored)
- {
- }
-
- return null;
- }
-
- @Nullable
- public static String getDisplayName(@NonNull Uri uri)
- {
- final String[] projection = new String[]{Document.COLUMN_DISPLAY_NAME};
- Uri documentUri = treeToDocument(uri);
- try (Cursor cursor = getContentResolver().query(documentUri, projection, null, null, null))
- {
- if (cursor != null && cursor.moveToFirst())
- {
- return cursor.getString(0);
- }
- }
- catch (SecurityException e)
- {
- Log.error("Tried to get display name of " + uri + " without permission");
- }
- catch (Exception ignored)
- {
- }
-
- return null;
- }
-
- @NonNull @Keep
- public static String[] getChildNames(@NonNull String uri, boolean recursive)
- {
- try
- {
- return getChildNames(unmangle(uri), recursive);
- }
- catch (Exception ignored)
- {
- }
-
- return new String[0];
- }
-
- @NonNull
- public static String[] getChildNames(@NonNull Uri uri, boolean recursive)
- {
- ArrayList<String> result = new ArrayList<>();
-
- ForEachChildCallback callback = new ForEachChildCallback()
- {
- @Override
- public void run(String displayName, String documentId, boolean isDirectory)
- {
- if (recursive && isDirectory)
- {
- forEachChild(uri, documentId, this);
- }
- else
- {
- result.add(displayName);
- }
- }
- };
-
- forEachChild(uri, DocumentsContract.getDocumentId(treeToDocument(uri)), callback);
-
- return result.toArray(new String[0]);
- }
-
- @NonNull @Keep
- public static String[] doFileSearch(@NonNull String directory, @NonNull String[] extensions,
- boolean recursive)
- {
- ArrayList<String> result = new ArrayList<>();
-
- try
- {
- Uri uri = unmangle(directory);
- String documentId = DocumentsContract.getDocumentId(treeToDocument(uri));
- boolean acceptAll = extensions.length == 0;
- Predicate<String> extensionCheck = (displayName) ->
- {
- String extension = FileBrowserHelper.getExtension(displayName, true);
- return extension != null && Arrays.stream(extensions).anyMatch(extension::equalsIgnoreCase);
- };
- doFileSearch(uri, directory, documentId, recursive, result, acceptAll, extensionCheck);
- }
- catch (Exception ignored)
- {
- }
-
- return result.toArray(new String[0]);
- }
-
- private static void doFileSearch(@NonNull Uri baseUri, @NonNull String path,
- @NonNull String documentId, boolean recursive, @NonNull List<String> resultOut,
- boolean acceptAll, @NonNull Predicate<String> extensionCheck)
- {
- forEachChild(baseUri, documentId, (displayName, childDocumentId, isDirectory) ->
- {
- String childPath = path + '/' + displayName;
- if (acceptAll || (!isDirectory && extensionCheck.test(displayName)))
- {
- resultOut.add(childPath);
- }
- if (recursive && isDirectory)
- {
- doFileSearch(baseUri, childPath, childDocumentId, recursive, resultOut, acceptAll,
- extensionCheck);
- }
- });
- }
-
- private interface ForEachChildCallback
- {
- void run(String displayName, String documentId, boolean isDirectory);
- }
-
- private static void forEachChild(@NonNull Uri uri, @NonNull String documentId,
- @NonNull ForEachChildCallback callback)
- {
- try
- {
- Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, documentId);
-
- final String[] projection = new String[]{Document.COLUMN_DISPLAY_NAME,
- Document.COLUMN_MIME_TYPE, Document.COLUMN_DOCUMENT_ID};
- try (Cursor cursor = getContentResolver().query(childrenUri, projection, null, null, null))
- {
- if (cursor != null)
- {
- while (cursor.moveToNext())
- {
- callback.run(cursor.getString(0), cursor.getString(2),
- Document.MIME_TYPE_DIR.equals(cursor.getString(1)));
- }
- }
- }
- }
- catch (SecurityException e)
- {
- Log.error("Tried to get children of " + uri + " without permission");
- }
- catch (Exception ignored)
- {
- }
- }
-
- @NonNull
- private static Uri getChild(@NonNull Uri parentUri, @NonNull String childName)
- throws FileNotFoundException, SecurityException
- {
- String parentId = DocumentsContract.getDocumentId(treeToDocument(parentUri));
- Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(parentUri, parentId);
-
- final String[] projection = new String[]{Document.COLUMN_DISPLAY_NAME,
- Document.COLUMN_DOCUMENT_ID};
- final String selection = Document.COLUMN_DISPLAY_NAME + "=?";
- final String[] selectionArgs = new String[]{childName};
- try (Cursor cursor = getContentResolver().query(childrenUri, projection, selection,
- selectionArgs, null))
- {
- if (cursor != null)
- {
- while (cursor.moveToNext())
- {
- // FileProvider seemingly doesn't support selections, so we have to manually filter here
- if (childName.equals(cursor.getString(0)))
- {
- return DocumentsContract.buildDocumentUriUsingTree(parentUri, cursor.getString(1));
- }
- }
- }
- }
- catch (SecurityException e)
- {
- Log.error("Tried to get child " + childName + " of " + parentUri + " without permission");
- }
- catch (Exception ignored)
- {
- }
-
- throw new FileNotFoundException(parentUri + "/" + childName);
- }
-
- /**
- * Since our C++ code was written under the assumption that it would be running under a filesystem
- * which supports normal paths, it appends a slash followed by a file name when it wants to access
- * a file in a directory. This function translates that into the type of URI that SAF requires.
- *
- * In order to detect whether a URI is mangled or not, we make the assumption that an
- * unmangled URI contains at least one % and does not contain any slashes after the last %.
- * This seems to hold for all common storage providers, but it is theoretically for a storage
- * provider to use URIs without any % characters.
- */
- @NonNull
- public static Uri unmangle(@NonNull String uri) throws FileNotFoundException, SecurityException
- {
- int lastComponentEnd = getLastComponentEnd(uri);
- int lastComponentStart = getLastComponentStart(uri, lastComponentEnd);
-
- if (lastComponentStart == 0)
- {
- return Uri.parse(uri.substring(0, lastComponentEnd));
- }
- else
- {
- Uri parentUri = unmangle(uri.substring(0, lastComponentStart));
- String childName = uri.substring(lastComponentStart, lastComponentEnd);
- return getChild(parentUri, childName);
- }
- }
-
- /**
- * Returns the last character which is not a slash.
- */
- private static int getLastComponentEnd(@NonNull String uri)
- {
- int i = uri.length();
- while (i > 0 && uri.charAt(i - 1) == '/')
- i--;
- return i;
- }
-
- /**
- * Scans backwards starting from lastComponentEnd and returns the index after the first slash
- * it finds, but only if there is a % before that slash and there is no % after it.
- */
- private static int getLastComponentStart(@NonNull String uri, int lastComponentEnd)
- {
- int i = lastComponentEnd;
- while (i > 0 && uri.charAt(i - 1) != '/')
- {
- i--;
- if (uri.charAt(i) == '%')
- return 0;
- }
-
- int j = i;
- while (j > 0)
- {
- j--;
- if (uri.charAt(j) == '%')
- return i;
- }
-
- return 0;
- }
-
- @NonNull
- private static Uri treeToDocument(@NonNull Uri uri)
- {
- if (isTreeUri(uri))
- {
- String documentId = DocumentsContract.getTreeDocumentId(uri);
- return DocumentsContract.buildDocumentUriUsingTree(uri, documentId);
- }
- else
- {
- return uri;
- }
- }
-
- /**
- * This is like DocumentsContract.isTreeUri, except it doesn't return true for URIs like
- * content://com.example/tree/12/document/24/. We want to treat those as documents, not trees.
- */
- private static boolean isTreeUri(@NonNull Uri uri)
- {
- final List<String> pathSegments = uri.getPathSegments();
- return pathSegments.size() == 2 && "tree".equals(pathSegments.get(0));
- }
-
- private static ContentResolver getContentResolver()
- {
- return DolphinApplication.getAppContext().getContentResolver();
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.kt
new file mode 100644
index 0000000000..b5e4456839
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.kt
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.utils
+
+import android.content.ContentResolver
+import android.net.Uri
+import android.provider.DocumentsContract
+import android.provider.DocumentsContract.Document
+import androidx.annotation.Keep
+import androidx.core.net.toUri
+import org.dolphinemu.dolphinemu.DolphinApplication
+import java.io.FileNotFoundException
+
+/*
+ We use a lot of "catch (Exception e)" in this class. This is for two reasons:
+
+ 1. We don't want any exceptions to escape to native code, as this leads to nasty crashes
+ that often don't have stack traces that make sense.
+
+ 2. The sheer number of different exceptions, both documented and undocumented. These include:
+ - FileNotFoundException when a file doesn't exist
+ - FileNotFoundException when using an invalid open mode (according to the documentation)
+ - IllegalArgumentException when using an invalid open mode (in practice with FileProvider)
+ - IllegalArgumentException when providing a tree where a document was expected and vice versa
+ - SecurityException when trying to access something the user hasn't granted us permission to
+ - UnsupportedOperationException when a URI specifies a storage provider that doesn't exist
+ */
+object ContentHandler {
+ @JvmStatic
+ fun isContentUri(pathOrUri: String): Boolean {
+ return pathOrUri.startsWith("content://")
+ }
+
+ @Keep
+ @JvmStatic
+ fun openFd(uri: String, mode: String): Int {
+ try {
+ return getContentResolver().openFileDescriptor(unmangle(uri), mode)!!.detachFd()
+ } catch (_: SecurityException) {
+ Log.error("Tried to open $uri without permission")
+ } catch (_: Exception) {
+ }
+
+ return -1
+ }
+
+ @Keep
+ @JvmStatic
+ fun delete(uri: String): Boolean {
+ try {
+ return DocumentsContract.deleteDocument(getContentResolver(), unmangle(uri))
+ } catch (_: FileNotFoundException) {
+ // Return true because we care about the file not being there, not the actual delete.
+ return true
+ } catch (_: SecurityException) {
+ Log.error("Tried to delete $uri without permission")
+ } catch (_: Exception) {
+ }
+
+ return false
+ }
+
+ @JvmStatic
+ fun exists(uri: String): Boolean {
+ try {
+ val documentUri = treeToDocument(unmangle(uri))
+ val projection = arrayOf(Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE)
+ getContentResolver().query(documentUri, projection, null, null, null)?.use { cursor ->
+ return cursor.count > 0
+ }
+ } catch (_: SecurityException) {
+ Log.error("Tried to check if $uri exists without permission")
+ } catch (_: Exception) {
+ }
+
+ return false
+ }
+
+ /**
+ * @return -1 if not found, -2 if directory, file size otherwise
+ */
+ @Keep
+ @JvmStatic
+ fun getSizeAndIsDirectory(uri: String): Long {
+ try {
+ val documentUri = treeToDocument(unmangle(uri))
+ val projection = arrayOf(Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE)
+ getContentResolver().query(documentUri, projection, null, null, null)?.use { cursor ->
+ if (cursor.moveToFirst()) {
+ return if (Document.MIME_TYPE_DIR == cursor.getString(0)) {
+ -2
+ } else {
+ if (cursor.isNull(1)) 0 else cursor.getLong(1)
+ }
+ }
+ }
+ } catch (_: SecurityException) {
+ Log.error("Tried to get metadata for $uri without permission")
+ } catch (_: Exception) {
+ }
+
+ return -1
+ }
+
+ @Keep
+ @JvmStatic
+ fun getDisplayName(uri: String): String? {
+ try {
+ return getDisplayName(unmangle(uri))
+ } catch (_: Exception) {
+ }
+
+ return null
+ }
+
+ @JvmStatic
+ fun getDisplayName(uri: Uri): String? {
+ val projection = arrayOf(Document.COLUMN_DISPLAY_NAME)
+ val documentUri = treeToDocument(uri)
+ try {
+ getContentResolver().query(documentUri, projection, null, null, null)?.use { cursor ->
+ if (cursor.moveToFirst()) {
+ return cursor.getString(0)
+ }
+ }
+ } catch (_: SecurityException) {
+ Log.error("Tried to get display name of $uri without permission")
+ } catch (_: Exception) {
+ }
+
+ return null
+ }
+
+ @Keep
+ @JvmStatic
+ fun getChildNames(uri: String, recursive: Boolean): Array<String> {
+ try {
+ return getChildNames(unmangle(uri), recursive)
+ } catch (_: Exception) {
+ }
+
+ return emptyArray()
+ }
+
+ @JvmStatic
+ fun getChildNames(uri: Uri, recursive: Boolean): Array<String> {
+ val result = ArrayList<String>()
+ fun addChildren(documentId: String) {
+ forEachChild(uri, documentId) { displayName, childDocumentId, isDirectory ->
+ if (recursive && isDirectory) {
+ addChildren(childDocumentId)
+ } else {
+ result.add(displayName)
+ }
+ }
+ }
+
+ addChildren(DocumentsContract.getDocumentId(treeToDocument(uri)))
+ return result.toTypedArray()
+ }
+
+ @Keep
+ @JvmStatic
+ fun doFileSearch(
+ directory: String, extensions: Array<String>, recursive: Boolean
+ ): Array<String> {
+ val result = ArrayList<String>()
+
+ try {
+ val uri = unmangle(directory)
+ val documentId = DocumentsContract.getDocumentId(treeToDocument(uri))
+ val acceptAll = extensions.isEmpty()
+ val extensionCheck: (String) -> Boolean = { displayName ->
+ val extension = FileBrowserHelper.getExtension(displayName, true)
+ extension != null && extensions.any { it.equals(extension, ignoreCase = true) }
+ }
+
+ fun addChildren(path: String, parentDocumentId: String) {
+ forEachChild(uri, parentDocumentId) { displayName, childDocumentId, isDirectory ->
+ val childPath = "$path/$displayName"
+ if (acceptAll || (!isDirectory && extensionCheck(displayName))) {
+ result.add(childPath)
+ }
+ if (recursive && isDirectory) {
+ addChildren(childPath, childDocumentId)
+ }
+ }
+ }
+
+ addChildren(directory, documentId)
+ } catch (_: Exception) {
+ }
+
+ return result.toTypedArray()
+ }
+
+ private fun forEachChild(
+ uri: Uri,
+ documentId: String,
+ callback: (displayName: String, documentId: String, isDirectory: Boolean) -> Unit
+ ) {
+ try {
+ val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, documentId)
+ val projection = arrayOf(
+ Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE, Document.COLUMN_DOCUMENT_ID
+ )
+ getContentResolver().query(childrenUri, projection, null, null, null)?.use { cursor ->
+ while (cursor.moveToNext()) {
+ callback(
+ cursor.getString(0),
+ cursor.getString(2),
+ Document.MIME_TYPE_DIR == cursor.getString(1)
+ )
+ }
+ }
+ } catch (_: SecurityException) {
+ Log.error("Tried to get children of $uri without permission")
+ } catch (_: Exception) {
+ }
+ }
+
+ @Throws(FileNotFoundException::class, SecurityException::class)
+ private fun getChild(parentUri: Uri, childName: String): Uri {
+ val parentId = DocumentsContract.getDocumentId(treeToDocument(parentUri))
+ val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(parentUri, parentId)
+
+ val projection = arrayOf(Document.COLUMN_DISPLAY_NAME, Document.COLUMN_DOCUMENT_ID)
+ val selection = Document.COLUMN_DISPLAY_NAME + "=?"
+ val selectionArgs = arrayOf(childName)
+ try {
+ getContentResolver().query(childrenUri, projection, selection, selectionArgs, null)
+ ?.use { cursor ->
+ while (cursor.moveToNext()) {
+ // FileProvider seemingly doesn't support selections, so we have to manually filter here
+ if (childName == cursor.getString(0)) {
+ return DocumentsContract.buildDocumentUriUsingTree(
+ parentUri, cursor.getString(1)
+ )
+ }
+ }
+ }
+ } catch (_: SecurityException) {
+ Log.error("Tried to get child $childName of $parentUri without permission")
+ } catch (_: Exception) {
+ }
+
+ throw FileNotFoundException("$parentUri/$childName")
+ }
+
+ /**
+ * Since our C++ code was written under the assumption that it would be running under a filesystem
+ * which supports normal paths, it appends a slash followed by a file name when it wants to access
+ * a file in a directory. This function translates that into the type of URI that SAF requires.
+ *
+ * In order to detect whether a URI is mangled or not, we make the assumption that an
+ * unmangled URI contains at least one % and does not contain any slashes after the last %.
+ * This seems to hold for all common storage providers, but it is theoretically possible for a
+ * storage provider to use URIs without any % characters.
+ */
+ @Throws(FileNotFoundException::class, SecurityException::class)
+ @JvmStatic
+ fun unmangle(uri: String): Uri {
+ val lastComponentEnd = getLastComponentEnd(uri)
+ val lastComponentStart = getLastComponentStart(uri, lastComponentEnd)
+
+ return if (lastComponentStart == 0) {
+ uri.substring(0, lastComponentEnd).toUri()
+ } else {
+ val parentUri = unmangle(uri.substring(0, lastComponentStart))
+ val childName = uri.substring(lastComponentStart, lastComponentEnd)
+ getChild(parentUri, childName)
+ }
+ }
+
+ /**
+ * Returns the last character which is not a slash.
+ */
+ private fun getLastComponentEnd(uri: String): Int {
+ var i = uri.length
+ while (i > 0 && uri[i - 1] == '/') {
+ i--
+ }
+ return i
+ }
+
+ /**
+ * Scans backwards starting from lastComponentEnd and returns the index after the first slash
+ * it finds, but only if there is a % before that slash and there is no % after it.
+ */
+ private fun getLastComponentStart(uri: String, lastComponentEnd: Int): Int {
+ var i = lastComponentEnd
+ while (i > 0 && uri[i - 1] != '/') {
+ i--
+ if (uri[i] == '%') {
+ return 0
+ }
+ }
+
+ var j = i
+ while (j > 0) {
+ j--
+ if (uri[j] == '%') {
+ return i
+ }
+ }
+
+ return 0
+ }
+
+ private fun treeToDocument(uri: Uri): Uri {
+ return if (isTreeUri(uri)) {
+ val documentId = DocumentsContract.getTreeDocumentId(uri)
+ DocumentsContract.buildDocumentUriUsingTree(uri, documentId)
+ } else {
+ uri
+ }
+ }
+
+ /**
+ * This is like DocumentsContract.isTreeUri, except it doesn't return true for URIs like
+ * content://com.example/tree/12/document/24/. We want to treat those as documents, not trees.
+ */
+ private fun isTreeUri(uri: Uri): Boolean {
+ val pathSegments = uri.pathSegments
+ return pathSegments.size == 2 && pathSegments[0] == "tree"
+ }
+
+ private fun getContentResolver(): ContentResolver {
+ return DolphinApplication.getAppContext().contentResolver
+ }
+}