From 5ed0463f39633661e3a712c8228d30bc58e0a44a Mon Sep 17 00:00:00 2001
From: "transifex-integration[bot]"
<43880903+transifex-integration[bot]@users.noreply.github.com>
Date: Mon, 17 Oct 2022 07:31:08 +0000
Subject: [PATCH 001/328] Apply translations in it
translation completed for the source file '/app/src/main/res/values/strings.xml'
on the 'it' language.
---
app/src/main/res/values-it/strings.xml | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 0373909391..f38bede2aa 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1,4 +1,4 @@
-
+
@@ -105,11 +108,12 @@
Material Escuro
Hora do Dia
Preto (para OLED)
+ Sistema (seguir tema do sistema)
- Pastas no início
- Arquivos no início
- Nada no início
+ Pastas primeiro
+ Arquivos primeiro
+ Nada no topo
Nome
Última modificação
@@ -149,7 +153,7 @@
Mostra as miniaturas dos aplicativos e imagens
Mostrar miniaturas
Mostrar arquivos e pastas ocultos
- Mostra a data e hora da última modificação do arquivo
+ Mostrar data e hora da última modificação
Mostrar última modificação
Mostra o tamanho dos arquivos e o número de itens das pastas
Mostrar tamanho
@@ -238,6 +242,7 @@
Escolha a pasta raíz do \t
\tpara conceder acesso de gravação para operar
Conexão SMB
+ Endereço IP do servidor
Endereço IP do servidor
Usuário
Senha
@@ -292,6 +297,7 @@
Parado
Confirme se está conectado com Wi-Fi
Domínio
+ Compartilhar caminho
Inválido %s
O servidor FTP está iniciando
Servidor FTP
@@ -424,6 +430,7 @@
Você precisará copiar este arquivo para armazenamento para abri-lo
Erro: Arquivo não encontrado
Erro E/S
+ Erro: Arquivo muito grande
Texto médio
Ler
Gravar
@@ -438,6 +445,7 @@
Conexão em nuvem
Nova conexão em nuvem
Dropbox
+ Google Drive™
OneDrive
Box
Obter plugin para nuvem
@@ -503,7 +511,13 @@
Acessos rápidos
Pasta
+ Favoritos da barra lateral
+ Mostrar favoritos
Favoritos
+ Adicionar favorito
+ Editar favorito
+ Excluir favorito?
+
O plugin Amaze Cloud não foi encontrado. Faça download a partir da Play Store.
Falha ao carregar a conexão. Por favor, abra o Amaze Cloud uma vez.
Deseja remover a conexão com a nuvem?
@@ -552,6 +566,8 @@
ATUALIZAR
Conexão SCP/SFTP
+ Conexão FTP
+ FTP usando conexão SSL
Porta
Não é possível determinar a autenticidade do servidor \'%1$s\'.\n\n%2$s chave da impressão digital é %3$s.\n\nToque em \"Sim\" para confirmar a identidade, caso contrário, toque em \"Não\".
Verificar servidor
@@ -595,11 +611,22 @@
Não há informações sobre este arquivo
Não foi possível, reabra a partir do último aplicativo
Algo deu errado, não há nada para abrir
+ O arquivo é muito longo então não pode ser editado, mas você pode ler os primeiros %d caracteres
O arquivo aberto é apenas para leitura.
Entendi!
Salvar arquivo criptografado como…
Salvar pasta criptografada como…
+ Usar formato de criptografia Amaze
+ AESCrypt é um formato de criptografia de arquivo de código aberto baseado na criptografia AES. Também é portátil, pode ser descriptografado com programas AESCrypt em outras plataformas.
+Site AESCrypt
]]>
+
+ O formato de criptografia do Amaze também é baseado em criptografia AES; mas diferente do formato AESCrypt, ele só pode ser descriptografado por este dispositivo; Além disso, você não deve desinstalar o Amaze File Manager deste dispositivo nem alterar a maneira de bloquear a tela, caso contrário não terá como recuperar o arquivo criptografado.
+ ]]>
Os arquivos criptografados devem usar \".aze\" como extensão.
+ Arquivos criptografados devem usar \".aes\" como extensão de arquivo.
+ Não é possível descriptografar \'%s\': \'%s\'
Não foi possível sobrescrever o arquivo.
Arquivo salvo como \'%s\'.
%d arquivos salvos.
@@ -663,9 +690,83 @@
Escolha uma aplicação diferente
Limpar cache
Limpa os apps de abertura padrão selecionados
+ Apenas leitura
Usar listagem legada para root
+ Se habilitado, usa método legado para listar arquivos
+ Arquivo RAR \"%s\" é um arquivo RAR v5 não suportado.
+ Você precisa reiniciar o servidor FTP para aplicar as mudanças.
Permissão negada
- Arquivos recentemente acessados pelo Amaze
+ Senha incorreta do arquivo.
+ Não é possível extrair \"%s\" para \"%s\".
+ Selecionar a preferência de arrastar e soltar (Experimental)
+ Pressionar e arrastar para mover ou copiar arquivos
+ Pressionar e arrastar para selecionar arquivos
+ Desabilitar
+ Escolha a operação para realizar
+ Lembrar para a próxima vez
+ Permitir acesso SAF para o servidor FTP
+ Já que essa é sua primeira vez rodando o servidor FTP, e você está usando o armazenamento interno como pasta compartilhada, por favor permita que o Amaze acesse o armazenamento por SAF.\n\n
+
+Você só precisa fazer isso uma vez, até a próxima vez que você selecionar um novo local para compartilhamento.
+ Desde o Android 10, o Android impôs restrições em certos diretórios, e eles precisam de acesso com o Storage Access Framework.\n\nPor favor permita que Amaze acesse o diretório de dentro do gerenciador de arquivos do sistema pressionando \"Permitir Acesso\".
+ Permitir acesso SAF
+ Usar sistema de arquivos legado
+ Habilite isso para suportar o uso de armazenamento externo de dispositivo em versões Android mais recentes
+ O local compartilhado do servidor FTP foi redefinido para armazenamento interno, pois você está voltando para a implementação do sistema de arquivos legado. Por favor, selecione um novo local usando o menu no canto superior direito conforme necessário.
+ O local compartilhado do servidor FTP foi redefinido para armazenamento interno
+ Você está escolhendo um local que requer acesso usuario root. Por favor habilitar o explorador root nas configurações se o seu dispositivo está rooteado.
+ Você está escolhendo um local que requer acesso usuario root, que habilita ler/escrever em todo o sistema de arquivos de seu dispositivo, o que é perigoso - acidentes podem \"brickar\" ou inutilizar o seu dispositivo.\n\nTem certeza?
+ Erro abrindo URI \"%s\" para leitura.
+ \"%s\" está vazio.
+ \"%s\" está corrompido.
+ \"%s\" está corrompido.\nMensagem do extrator de arquivos:\"%s\"
+ Arquivo não pôde ser aberto por esse aplicativo
+ Criar uma nova conexão Google Drive™ para gerenciar arquivos de dentro do gerenciador de arquivos Amaze
+ Autenticar para usar o Google Drive™
+ Mostrar aplicativos do sistema
+ Aplicativos do sistema
+ Inverter seleção
+ Selecionar por tipo
+ Selecionar por data
+ Selecionar similares
+ Erro ao adquirir lista de produtos pelo Google Play.
+ Desde o Android 11, o Google requer que Gerenciadores de Arquivos peçam permissão para administrar todos arquivos no dispositivo. Mais detalhes aqui.
+ \n\nO Gerenciador de Arquivos Amaze precisa dessa permissão também. Depois de apertar "Grant" aqui, por favor selecione Permitir acesso para acessar todos arquivosna próxima tela.
+\n\nCancelar esse diálogo irá fechar o aplicativo.
+ Aplicativos de usuários
+ Aparência
+ Comportamento
+ Personalizar tema, cores e aparência do gerenciador de arquivos
+ Controlar quais informações mostrar, personalizar a barra lateral e alterar preferência da interface.
+ Modificar comportamentos como pesquisa avançada e abrir arquivos como uma nova tarefa
+ Configurar senha e criptografia
+
+ Arquivos acessados através do Amaze recentemente
Arquivos criados ou modificados recentemente
+ Não foi encontrada forma de abrir arquivo
+ Não foi possível processar o arquivo
+ Analizar Armazenamento
+ Você está sendo redirecionado para Amaze File Utilities. É a nossa oferta que tem visualizador audio/vídeo/documentos, que permite analizar armazenamento por imagens escuras/borradas, arquivos duplicados, e transferência por Wifi P2P e mais.\nNos apoie ao experimentar.
+ Baixar
+ Wi-Fi P2P
+ Obrigado por instalar Amaze File Utilities. Você está sendo redirecionado para o app.
+ Visualizador de Imagens Amaze
+ Leitor de Vídeo Amaze
+ Leitor de Música Amaze
+ Visualizador de Pdf Amaze
+ Visualizador de Epub Amaze
+ Visualizador de Doc Amaze
+ Compartilhar logs
+ Compartilhar logs capturados via email / telegram
+ Abrir com Amaze
+ Confirmação
+ Tem certeza de que deseja abrir o seguinte arquivo?\n\nNome:\n%s\n\nLocal:\n%s\n\nTamanho:\n%s\n\nMD5:\n%s\n\nSHA256:\n%s\n\n
+ Pelos requerimentos da política de privacidade do Google Play, apps não são permitidos atualizarem a si mesmos. Por favor atualize o aplicativo através da Google Play.
+ Não foi possível abrir informações do pacote do arquivo \"%s\". Ou o arquivo especificado não é um APK, ou o arquivo está corrompido.
+ Excluir confirmação
+ Pedir confirmação antes de apagar arquivos. Desabilitar essa opção é perigoso!
+ FTP com segurança
+ Não disponível
+ Amaze Cloud Plugin não é compatível com a versão F-Droid. Por favor, use a versão da Play Store do Amaze.
From 89fee6a5f67b918e5a4e3dbd71322a4b5f4db19e Mon Sep 17 00:00:00 2001
From: "transifex-integration[bot]"
<43880903+transifex-integration[bot]@users.noreply.github.com>
Date: Sat, 26 Nov 2022 07:33:12 +0000
Subject: [PATCH 003/328] Apply translations in it
translation completed for the source file '/app/src/main/res/values/strings.xml'
on the 'it' language.
---
app/src/main/res/values-it/strings.xml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index f38bede2aa..52398e0a8e 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -731,6 +731,10 @@ Dovrai farlo solo questa volta, fino a quando non sceglierai un percorso diverso
Chiede una conferma prima di cancellare file. La disabilitazione di questa funzione è altamente sconsigliata!
FTP sicuro
Non disponibile
+ \"%s\n%s liberi di %s
Il plugin Amaze Cloud non è supportato dalla versione F-Droid. Si prega di usare la versione di Amaze disponibile sul Play Store.
+ Disabilita filtri intent del player
+ Disabilita i filtri intent del player Amaze nel selettore dei file
+ Nessuna app trovata per gestire questo intent. DocumentsUI è installato?
From 9f9c67f3193cb5bac790b0a7410acc2a17bcf1cf Mon Sep 17 00:00:00 2001
From: "transifex-integration[bot]"
<43880903+transifex-integration[bot]@users.noreply.github.com>
Date: Sun, 27 Nov 2022 13:24:06 +0000
Subject: [PATCH 004/328] Apply translations in he
translation completed for the source file '/app/src/main/res/values/strings.xml'
on the 'he' language.
---
app/src/main/res/values-he/strings.xml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 7922ffc25b..ded1e39a4e 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -779,6 +779,10 @@
לבקש אישור בטרם מחיקת קבצים. השבתת האפשרות לא מומלצת כלל!
FTP מאובטח
לא זמין
+ \"%s\n%s פנויים מתוך %s
אין תמיכה בתוסף Amaze Cloud בגרסת ה־F-Droid. נא להשתמש בגרסת חנות ה־Play של Amaze.
+ השבתת מסנני Intent של הנגן
+ משבית את נגני המדיה המובנים של Amaze בבוחר הקבצים
+ לא נמצא יישומון לטיפול ב־Intent הזה. האם מותקן אצלך DocumentsUI?
From 8b3d0611e986073f5cb791314de8a8012bc72ab0 Mon Sep 17 00:00:00 2001
From: "transifex-integration[bot]"
<43880903+transifex-integration[bot]@users.noreply.github.com>
Date: Wed, 30 Nov 2022 14:14:14 +0000
Subject: [PATCH 005/328] Apply translations in de
translation completed for the source file '/app/src/main/res/values/strings.xml'
on the 'de' language.
---
app/src/main/res/values-de/strings.xml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index be9b9d62f2..501cfc2caa 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -106,7 +106,7 @@
Hell
Dunkel
- Tageslicht
+ Gemäß Tageszeit
Schwarz (für OLED)
System (folgt dem System)
@@ -729,6 +729,10 @@ Sie müssen diesen Schritt nur einmalig ausführen, bis Sie einen neuen Ort für
Sicherheitsabfrage vor dem Löschen von Dateien. Dies sollte möglichst nicht deaktiviert werden.
Secure FTP
Nicht verfügbar
+ \"%s\n%s von %s frei
Das Amaze Cloud-Plug-In wird von der F-Droid-Version nicht unterstützt. Bitte verwenden Sie die Play Store-Version von Amaze.
+ Aktionsfilter für Player deaktivieren
+ In Amaze integrierten Mediaplayer für Dateiauswahl deaktivieren
+ Keine App für diese Aktion gefunden. Haben Sie DocumentsUI installiert?
From d039c8b25480ba204f0dd7f215806034db3fe3ed Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Sat, 11 Feb 2023 21:04:07 +0530
Subject: [PATCH 006/328] store recent search items
---
.../PreferencesConstants.kt | 3 +
.../ui/views/appbar/SearchView.java | 114 +++++++++++++++---
app/src/main/res/layout-v21/layout_search.xml | 96 +++++++++++----
.../main/res/layout-w720dp/layout_search.xml | 90 ++++++++++----
app/src/main/res/layout/layout_search.xml | 92 ++++++++++----
app/src/main/res/values/styles.xml | 8 ++
6 files changed, 319 insertions(+), 84 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preferencefragments/PreferencesConstants.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preferencefragments/PreferencesConstants.kt
index 346066b6ad..91978ffd33 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preferencefragments/PreferencesConstants.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preferencefragments/PreferencesConstants.kt
@@ -92,6 +92,9 @@ object PreferencesConstants {
const val PREFERENCE_EXPORT_SETTINGS = "export_settings"
const val PREFERENCE_IMPORT_SETTINGS = "import_settings"
+ // recent search items
+ const val PREFERENCE_RECENT_SEARCH_ITEMS = "recent_searches"
+
// others
const val PREFERENCE_CURRENT_TAB = ""
const val PREFERENCE_BOOKMARKS_ADDED = "books_added"
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index f6b1345d13..6a45f34055 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -23,25 +23,35 @@
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
+import java.util.ArrayList;
+
import com.amaze.filemanager.R;
import com.amaze.filemanager.ui.activities.MainActivity;
+import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
import com.amaze.filemanager.ui.theme.AppTheme;
import com.amaze.filemanager.utils.Utils;
+import com.google.android.material.chip.Chip;
+import com.google.android.material.chip.ChipGroup;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.PorterDuff;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
-import android.widget.RelativeLayout;
+import android.widget.TextView;
import androidx.appcompat.widget.AppCompatEditText;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
+import androidx.preference.PreferenceManager;
/**
* SearchView, a simple view to search
@@ -50,25 +60,33 @@
*/
public class SearchView {
- private MainActivity mainActivity;
- private AppBar appbar;
+ private final MainActivity mainActivity;
+ private final AppBar appbar;
+
+ private final ConstraintLayout searchViewLayout;
+ private final AppCompatEditText searchViewEditText;
+ private final ImageView clearImageView, backImageView;
+ private final TextView recentHintTV;
+ private final ChipGroup recentChipGroup;
- private RelativeLayout searchViewLayout;
- private AppCompatEditText searchViewEditText;
- private ImageView clearImageView;
- private ImageView backImageView;
+ private final SearchListener searchListener;
private boolean enabled = false;
- public SearchView(
- final AppBar appbar, final MainActivity a, final SearchListener searchListener) {
- mainActivity = a;
+ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener searchListener) {
+
+ this.mainActivity = mainActivity;
+ this.searchListener = searchListener;
this.appbar = appbar;
- searchViewLayout = a.findViewById(R.id.search_view);
- searchViewEditText = a.findViewById(R.id.search_edit_text);
- clearImageView = a.findViewById(R.id.search_close_btn);
- backImageView = a.findViewById(R.id.img_view_back);
+ searchViewLayout = mainActivity.findViewById(R.id.search_view);
+ searchViewEditText = mainActivity.findViewById(R.id.search_edit_text);
+ clearImageView = mainActivity.findViewById(R.id.search_close_btn);
+ backImageView = mainActivity.findViewById(R.id.img_view_back);
+ recentChipGroup = mainActivity.findViewById(R.id.searchRecentItemsChipGroup);
+ recentHintTV = mainActivity.findViewById(R.id.searchRecentHintTV);
+
+ initRecentSearches(mainActivity);
clearImageView.setOnClickListener(v -> searchViewEditText.setText(""));
@@ -77,16 +95,76 @@ public SearchView(
searchViewEditText.setOnEditorActionListener(
(v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- searchListener.onSearch(searchViewEditText.getText().toString());
+
+ String s = searchViewEditText.getText().toString();
+
+ searchListener.onSearch(s);
appbar.getSearchView().hideSearchView();
+
+ String preferenceString =
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .getString(PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS, null);
+
+ ArrayList recentSearches =
+ preferenceString != null
+ ? new Gson()
+ .fromJson(preferenceString, new TypeToken>() {}.getType())
+ : new ArrayList<>();
+
+ recentSearches.add(s);
+
+ if (recentSearches.size() > 5) recentSearches.remove(0);
+
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .edit()
+ .putString(
+ PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS,
+ new Gson().toJson(recentSearches))
+ .apply();
+
+ initRecentSearches(mainActivity);
+
return true;
}
return false;
});
- initSearchViewColor(a);
- // searchViewEditText.setTextColor(Utils.getColor(this, android.R.color.black));
- // searchViewEditText.setHintTextColor(Color.parseColor(ThemedActivity.accentSkin));
+ initSearchViewColor(mainActivity);
+ }
+
+ private void initRecentSearches(Context context) {
+
+ String preferenceString =
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS, null);
+
+ if (preferenceString == null) {
+ recentHintTV.setVisibility(View.GONE);
+ recentChipGroup.setVisibility(View.GONE);
+ return;
+ }
+
+ recentHintTV.setVisibility(View.VISIBLE);
+ recentChipGroup.setVisibility(View.VISIBLE);
+
+ recentChipGroup.removeAllViews();
+
+ ArrayList recentSearches =
+ new Gson().fromJson(preferenceString, new TypeToken>() {}.getType());
+
+ for (String string : recentSearches) {
+ Chip chip = new Chip(new ContextThemeWrapper(context, R.style.ChipStyle));
+
+ chip.setText(string);
+
+ recentChipGroup.addView(chip);
+
+ chip.setOnClickListener(
+ v -> {
+ searchListener.onSearch(((Chip) v).getText().toString());
+ appbar.getSearchView().hideSearchView();
+ });
+ }
}
/** show search view with a circular reveal animation */
diff --git a/app/src/main/res/layout-v21/layout_search.xml b/app/src/main/res/layout-v21/layout_search.xml
index b37cbaa518..3757da7b63 100644
--- a/app/src/main/res/layout-v21/layout_search.xml
+++ b/app/src/main/res/layout-v21/layout_search.xml
@@ -1,47 +1,97 @@
-
+ android:visibility="gone">
+ android:layout_marginRight="@dimen/search_view_back_margin_left_right"
+ android:background="@drawable/ripple"
+ android:src="@drawable/ic_arrow_back_black_24dp"
+ app:layout_constraintBottom_toBottomOf="@id/search_edit_text"
+ app:layout_constraintEnd_toStartOf="@id/search_edit_text"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@id/search_edit_text" />
+
+ app:layout_constraintBottom_toTopOf="@id/searchRecentHintTV"
+ app:layout_constraintEnd_toStartOf="@id/search_close_btn"
+ app:layout_constraintStart_toEndOf="@id/img_view_back"
+ app:layout_constraintTop_toTopOf="parent" />
+
-
\ No newline at end of file
+ android:layout_marginRight="@dimen/search_view_info_margin_left_right"
+ android:background="@drawable/ripple"
+ android:src="@drawable/ic_close_black_24dp"
+ app:layout_constraintBottom_toBottomOf="@id/search_edit_text"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/search_edit_text"
+ app:layout_constraintTop_toTopOf="@id/search_edit_text" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-w720dp/layout_search.xml b/app/src/main/res/layout-w720dp/layout_search.xml
index 09d90c2631..0da790bdf4 100644
--- a/app/src/main/res/layout-w720dp/layout_search.xml
+++ b/app/src/main/res/layout-w720dp/layout_search.xml
@@ -1,46 +1,94 @@
-
+ android:visibility="gone">
+ android:layout_marginRight="@dimen/search_view_back_margin_left_right"
+ android:src="@drawable/ic_arrow_back_black_24dp"
+ app:layout_constraintBottom_toBottomOf="@id/search_edit_text"
+ app:layout_constraintEnd_toStartOf="@id/search_edit_text"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@id/search_edit_text" />
+
+ app:layout_constraintBottom_toTopOf="@id/searchRecentHintTV"
+ app:layout_constraintEnd_toStartOf="@id/search_close_btn"
+ app:layout_constraintStart_toEndOf="@id/img_view_back"
+ app:layout_constraintTop_toTopOf="parent" />
+
-
\ No newline at end of file
+ android:src="@drawable/ic_close_black_24dp"
+ app:layout_constraintBottom_toBottomOf="@id/search_edit_text"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/search_edit_text"
+ app:layout_constraintTop_toTopOf="@id/search_edit_text" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_search.xml b/app/src/main/res/layout/layout_search.xml
index 5c32fc38bb..0da790bdf4 100644
--- a/app/src/main/res/layout/layout_search.xml
+++ b/app/src/main/res/layout/layout_search.xml
@@ -1,46 +1,94 @@
-
-
+ android:visibility="gone">
+ android:layout_marginRight="@dimen/search_view_back_margin_left_right"
+ android:src="@drawable/ic_arrow_back_black_24dp"
+ app:layout_constraintBottom_toBottomOf="@id/search_edit_text"
+ app:layout_constraintEnd_toStartOf="@id/search_edit_text"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@id/search_edit_text" />
+
+ app:layout_constraintBottom_toTopOf="@id/searchRecentHintTV"
+ app:layout_constraintEnd_toStartOf="@id/search_close_btn"
+ app:layout_constraintStart_toEndOf="@id/img_view_back"
+ app:layout_constraintTop_toTopOf="parent" />
+
-
+ android:layout_marginRight="@dimen/search_view_info_margin_left_right"
+ android:src="@drawable/ic_close_black_24dp"
+ app:layout_constraintBottom_toBottomOf="@id/search_edit_text"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/search_edit_text"
+ app:layout_constraintTop_toTopOf="@id/search_edit_text" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 88b8898f4d..747c2be575 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -317,4 +317,12 @@
+
+
+
From d0503942bcded27bbb3c04845a638df7fecb860c Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sat, 18 Feb 2023 11:40:45 +0800
Subject: [PATCH 007/328] SDK 33 adaptations + lib upgrades
It has to happen anyway.
- Compile with SDK 33
- Use Kotlin 1.8
- Use Java 11
- Library upgrades
- Fix syntax and API problems brought by the upgrade
---
app/build.gradle | 19 +++++++++---------
.../ui/base/BaseBottomSheetFragment.kt | 4 ++--
.../ui/views/drawer/HasherOfMenuItem.kt | 2 +-
.../database/MigrationTestHelper.java | 14 ++++++-------
build.gradle | 20 +++++++++----------
commons_compress_7z/build.gradle | 2 +-
file_operations/build.gradle | 4 ++--
7 files changed, 32 insertions(+), 33 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 34cd3e4b97..c6a4ccd3b7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ apply plugin: 'com.hiya.jacoco-android'
apply plugin: "com.starter.easylauncher"
android {
- compileSdkVersion 31
+ compileSdkVersion 33
packagingOptions {
resources {
excludes += ['proguard-project.txt', 'project.properties', 'META-INF/LICENSE.txt', 'META-INF/LICENSE', 'META-INF/NOTICE.txt', 'META-INF/NOTICE', 'META-INF/DEPENDENCIES.txt', 'META-INF/DEPENDENCIES']
@@ -17,7 +17,7 @@ android {
defaultConfig {
applicationId "com.amaze.filemanager"
minSdkVersion 19
- targetSdkVersion 31
+ targetSdkVersion 33
versionCode 118
versionName "3.8.5"
multiDexEnabled true
@@ -85,13 +85,12 @@ android {
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
- useIR = true
+ jvmTarget = '11'
}
testOptions {
@@ -254,11 +253,11 @@ dependencies {
configurations.all {
resolutionStrategy {
dependencySubstitution {
- substitute module("commons-logging:commons-logging-api:1.1") with module("commons-logging:commons-logging:1.1.1")
- substitute module("com.android.support:support-annotations:27.1.1") with module("com.android.support:support-annotations:27.0.2")
+ substitute module("commons-logging:commons-logging-api:1.1") using module("commons-logging:commons-logging:1.1.1")
+ substitute module("com.android.support:support-annotations:27.1.1") using module("com.android.support:support-annotations:27.0.2")
// These two lines are added to prevent possible class clashes between awaitility (which uses hamcrest 2.1) and junit (which uses hamcrest 1.3).
- substitute module('org.hamcrest:hamcrest-core:1.3') with module("org.hamcrest:hamcrest:2.1")
- substitute module('org.hamcrest:hamcrest-library:1.3') with module("org.hamcrest:hamcrest:2.1")
+ substitute module('org.hamcrest:hamcrest-core:1.3') using module("org.hamcrest:hamcrest:2.1")
+ substitute module('org.hamcrest:hamcrest-library:1.3') using module("org.hamcrest:hamcrest:2.1")
}
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt
index 78c3b6449f..c8e6551fa8 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt
@@ -39,7 +39,7 @@ open class BaseBottomSheetFragment : BottomSheetDialogFragment() {
* Initializes bottom sheet ui resources based on current theme
*/
fun initDialogResources(rootView: View) {
- when ((activity as ThemedActivity?)!!.appTheme!!) {
+ when ((requireActivity() as ThemedActivity).appTheme!!) {
AppTheme.DARK -> {
rootView.setBackgroundDrawable(
context?.resources?.getDrawable(
@@ -54,7 +54,7 @@ open class BaseBottomSheetFragment : BottomSheetDialogFragment() {
)
)
}
- AppTheme.LIGHT, AppTheme.TIMED -> {
+ AppTheme.LIGHT, AppTheme.TIMED, AppTheme.SYSTEM -> {
rootView
.setBackgroundDrawable(
context?.resources?.getDrawable(
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt
index 1e09c91473..bbf04494d1 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt
@@ -28,7 +28,7 @@ import android.view.MenuItem
data class HasherOfMenuItem(
val groupId: Int,
val itemId: Int,
- val title: CharSequence,
+ val title: CharSequence?,
val ordering: Int
)
diff --git a/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java b/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java
index d5dcacc0c7..e5094cfa30 100644
--- a/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java
+++ b/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java
@@ -477,7 +477,7 @@ static class MigratingDelegate extends MigrationTestHelper.RoomOpenHelperDelegat
}
@Override
- protected void createAllTables(SupportSQLiteDatabase database) {
+ public void createAllTables(SupportSQLiteDatabase database) {
throw new UnsupportedOperationException(
"Was expecting to migrate but received create."
+ "Make sure you have created the database first.");
@@ -485,7 +485,7 @@ protected void createAllTables(SupportSQLiteDatabase database) {
@NonNull
@Override
- protected RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
+ public RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
final Map tables = mDatabaseBundle.getEntitiesByTableName();
for (EntityBundle entity : tables.values()) {
if (entity instanceof FtsEntityBundle) {
@@ -548,7 +548,7 @@ static class CreatingDelegate extends MigrationTestHelper.RoomOpenHelperDelegate
}
@Override
- protected void createAllTables(SupportSQLiteDatabase database) {
+ public void createAllTables(SupportSQLiteDatabase database) {
for (String query : mDatabaseBundle.buildCreateQueries()) {
database.execSQL(query);
}
@@ -556,7 +556,7 @@ protected void createAllTables(SupportSQLiteDatabase database) {
@NonNull
@Override
- protected RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
+ public RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
throw new UnsupportedOperationException(
"This open helper just creates the database but" + " it received a migration request.");
}
@@ -571,14 +571,14 @@ abstract static class RoomOpenHelperDelegate extends RoomOpenHelper.Delegate {
}
@Override
- protected void dropAllTables(SupportSQLiteDatabase database) {
+ public void dropAllTables(SupportSQLiteDatabase database) {
throw new UnsupportedOperationException("cannot drop all tables in the test");
}
@Override
- protected void onCreate(SupportSQLiteDatabase database) {}
+ public void onCreate(SupportSQLiteDatabase database) {}
@Override
- protected void onOpen(SupportSQLiteDatabase database) {}
+ public void onOpen(SupportSQLiteDatabase database) {}
}
}
diff --git a/build.gradle b/build.gradle
index 94f25bf6b9..18909ccf8e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,23 +2,23 @@
buildscript {
ext {
- kotlin_version = "1.6.10"
+ kotlin_version = "1.8.0"
robolectricVersion = '4.9'
glideVersion = '4.11.0'
sshjVersion = '0.34.0'
jcifsVersion = '2.1.6'
fabSpeedDialVersion = '3.1.1'
- roomVersion = '2.4.3'
+ roomVersion = '2.5.0'
bouncyCastleVersion = '1.70'
awaitilityVersion = "3.1.6"
androidXCoreVersion = "1.7.0"
- androidMaterialVersion = "1.4.0" // Upgrade to 1.5 requires targetSdkVersion 31
- androidXFragmentVersion = "1.4.1"
- androidXAppCompatVersion = "1.4.1"
- androidXAnnotationVersion = "1.3.0"
+ androidMaterialVersion = "1.5.0" // Upgrade to 1.5 requires targetSdkVersion 31
+ androidXFragmentVersion = "1.5.5"
+ androidXAppCompatVersion = "1.6.1"
+ androidXAnnotationVersion = "1.5.0"
androidXPrefVersion = "1.2.0"
- androidXTestVersion = "1.4.0"
- androidXTestExtVersion = "1.1.3"
+ androidXTestVersion = "1.5.0"
+ androidXTestExtVersion = "1.1.5"
uiAutomatorVersion = "2.2.0"
junitVersion = "4.13.2"
slf4jVersion = "1.7.25"
@@ -27,7 +27,7 @@ buildscript {
androidBillingVersion = "5.0.0"
junrarVersion = "7.4.0"
zip4jVersion = "2.6.4"
- espressoVersion = "3.4.0"
+ espressoVersion = "3.5.1"
materialDialogsVersion = "0.9.6.0"
jacocoVersion = "0.8.7"
commonsCompressVersion = "1.22"
@@ -66,7 +66,7 @@ allprojects {
maven { url "https://jcenter.bintray.com" }
}
tasks.withType(Test) {
- maxParallelForks = 4
+ maxParallelForks = 8
maxHeapSize = "2g"
forkEvery = 4
failFast = true
diff --git a/commons_compress_7z/build.gradle b/commons_compress_7z/build.gradle
index 0b7e3a9e9a..428e80db73 100644
--- a/commons_compress_7z/build.gradle
+++ b/commons_compress_7z/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 28
+ compileSdkVersion 33
defaultConfig {
minSdkVersion 19
diff --git a/file_operations/build.gradle b/file_operations/build.gradle
index eadddd661e..47cefa5e63 100644
--- a/file_operations/build.gradle
+++ b/file_operations/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 30
+ compileSdkVersion 33
defaultConfig {
minSdkVersion 19
@@ -51,7 +51,7 @@ android {
}
kotlinOptions {
- useIR = true
+ jvmTarget = '11'
}
}
From 71db59f6f9b7109112830705d4da0bdcd0e225ce Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 21 Feb 2023 23:07:30 +0530
Subject: [PATCH 008/328] skip repeated queries and empty strings
---
.../java/com/amaze/filemanager/ui/views/appbar/SearchView.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 6a45f34055..c9d858c863 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -111,6 +111,8 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
.fromJson(preferenceString, new TypeToken>() {}.getType())
: new ArrayList<>();
+ if (s.isEmpty() || recentSearches.contains(s)) return false;
+
recentSearches.add(s);
if (recentSearches.size() > 5) recentSearches.remove(0);
From c02806ebba6dffc240ee3fbeb759dc8ed0244f3f Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 22 Feb 2023 22:01:21 +0530
Subject: [PATCH 009/328] chore: fix codady
---
.../com/amaze/filemanager/ui/views/appbar/SearchView.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index c9d858c863..efb717407c 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -65,7 +65,10 @@ public class SearchView {
private final ConstraintLayout searchViewLayout;
private final AppCompatEditText searchViewEditText;
- private final ImageView clearImageView, backImageView;
+
+ private final ImageView clearImageView;
+ private final ImageView backImageView;
+
private final TextView recentHintTV;
private final ChipGroup recentChipGroup;
From 0657f9dd5a52b6fbc09cd5d266b0103ef0b02b24 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Thu, 23 Feb 2023 12:38:41 +0530
Subject: [PATCH 010/328] chore: fix failing CI
---
app/src/main/res/layout/layout_search.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/src/main/res/layout/layout_search.xml b/app/src/main/res/layout/layout_search.xml
index 0da790bdf4..04bc137d5e 100644
--- a/app/src/main/res/layout/layout_search.xml
+++ b/app/src/main/res/layout/layout_search.xml
@@ -9,17 +9,17 @@
android:gravity="center_vertical"
android:visibility="gone">
-
+ app:layout_constraintTop_toTopOf="@id/search_edit_text"
+ app:srcCompat="@drawable/ic_arrow_back_black_24dp" />
-
+ app:layout_constraintTop_toTopOf="@id/search_edit_text"
+ app:srcCompat="@drawable/ic_close_black_24dp" />
Date: Mon, 13 Mar 2023 20:26:59 +0530
Subject: [PATCH 011/328] [WIP] simple search
---
.../adapters/SearchRecyclerViewAdapter.kt | 89 +++++++++
.../ui/views/appbar/SearchView.java | 128 +++++++++---
app/src/main/res/layout-v21/layout_search.xml | 185 +++++++++++-------
.../main/res/layout-w720dp/layout_search.xml | 168 +++++++++-------
app/src/main/res/layout/layout_search.xml | 169 +++++++++-------
app/src/main/res/layout/search_row_item.xml | 69 +++++++
6 files changed, 554 insertions(+), 254 deletions(-)
create mode 100644 app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
create mode 100644 app/src/main/res/layout/search_row_item.xml
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
new file mode 100644
index 0000000000..b9e7eefae5
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.adapters
+
+import android.text.format.Formatter
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.amaze.filemanager.R
+import com.amaze.filemanager.filesystem.HybridFileParcelable
+import com.amaze.filemanager.utils.Utils
+import java.util.*
+
+class SearchRecyclerViewAdapter :
+ ListAdapter(
+
+ object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: HybridFileParcelable,
+ newItem: HybridFileParcelable
+ ): Boolean {
+ return oldItem.path == newItem.path && oldItem.name == newItem.name
+ }
+
+ override fun areContentsTheSame(
+ oldItem: HybridFileParcelable,
+ newItem: HybridFileParcelable
+ ): Boolean {
+ return oldItem.path == newItem.path && oldItem.name == newItem.name
+ }
+ }
+ ) {
+ override fun onCreateViewHolder(parent: ViewGroup, type: Int): ViewHolder {
+ val v: View = LayoutInflater.from(parent.context)
+ .inflate(R.layout.search_row_item, parent, false)
+ return ViewHolder(v)
+ }
+
+ override fun onBindViewHolder(holder: SearchRecyclerViewAdapter.ViewHolder, position: Int) {
+ val item = getItem(position)
+
+ holder.fileNameTV.text = item.name
+
+ holder.dateTV.text = Utils.getDate(holder.itemView.context, item.date)
+
+ if (!item.isDirectory) {
+ holder.sizeTV.text = Formatter.formatFileSize(holder.itemView.context, item.size)
+ }
+ }
+
+ inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+
+ val fileNameTV: TextView
+ val dateTV: TextView
+ val sizeTV: TextView
+
+ init {
+
+ view.setOnClickListener {
+ }
+
+ fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
+ dateTV = view.findViewById(R.id.searchItemDateTV)
+ sizeTV = view.findViewById(R.id.searchItemSizeTV)
+ }
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index efb717407c..4285ac71c6 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -22,10 +22,14 @@
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
+import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES;
import java.util.ArrayList;
import com.amaze.filemanager.R;
+import com.amaze.filemanager.adapters.SearchRecyclerViewAdapter;
+import com.amaze.filemanager.filesystem.HybridFileParcelable;
+import com.amaze.filemanager.filesystem.root.ListFilesCommand;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
import com.amaze.filemanager.ui.theme.AppTheme;
@@ -37,6 +41,7 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PorterDuff;
import android.view.ContextThemeWrapper;
@@ -49,9 +54,10 @@
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatEditText;
-import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
+import androidx.core.widget.NestedScrollView;
import androidx.preference.PreferenceManager;
+import androidx.recyclerview.widget.RecyclerView;
/**
* SearchView, a simple view to search
@@ -63,16 +69,20 @@ public class SearchView {
private final MainActivity mainActivity;
private final AppBar appbar;
- private final ConstraintLayout searchViewLayout;
+ private final NestedScrollView searchViewLayout;
private final AppCompatEditText searchViewEditText;
private final ImageView clearImageView;
private final ImageView backImageView;
private final TextView recentHintTV;
+ private final TextView searchResultsHintTV;
+
private final ChipGroup recentChipGroup;
+ private final RecyclerView recyclerView;
private final SearchListener searchListener;
+ private final SearchRecyclerViewAdapter searchRecyclerViewAdapter;
private boolean enabled = false;
@@ -88,10 +98,21 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
backImageView = mainActivity.findViewById(R.id.img_view_back);
recentChipGroup = mainActivity.findViewById(R.id.searchRecentItemsChipGroup);
recentHintTV = mainActivity.findViewById(R.id.searchRecentHintTV);
+ searchResultsHintTV = mainActivity.findViewById(R.id.searchResultsHintTV);
+ recyclerView = mainActivity.findViewById(R.id.searchRecyclerView);
initRecentSearches(mainActivity);
- clearImageView.setOnClickListener(v -> searchViewEditText.setText(""));
+ searchRecyclerViewAdapter = new SearchRecyclerViewAdapter();
+ recyclerView.setAdapter(searchRecyclerViewAdapter);
+
+ clearRecyclerView();
+
+ clearImageView.setOnClickListener(
+ v -> {
+ searchViewEditText.setText("");
+ clearRecyclerView();
+ });
backImageView.setOnClickListener(v -> appbar.getSearchView().hideSearchView());
@@ -99,42 +120,77 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
(v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- String s = searchViewEditText.getText().toString();
+ String s = searchViewEditText.getText().toString().trim();
- searchListener.onSearch(s);
- appbar.getSearchView().hideSearchView();
+ search(s);
- String preferenceString =
- PreferenceManager.getDefaultSharedPreferences(mainActivity)
- .getString(PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS, null);
+ saveRecentPreference(s);
- ArrayList recentSearches =
- preferenceString != null
- ? new Gson()
- .fromJson(preferenceString, new TypeToken>() {}.getType())
- : new ArrayList<>();
+ return true;
+ }
+ return false;
+ });
- if (s.isEmpty() || recentSearches.contains(s)) return false;
+ initSearchViewColor(mainActivity);
+ }
- recentSearches.add(s);
+ private void search(String s) {
- if (recentSearches.size() > 5) recentSearches.remove(0);
+ clearRecyclerView();
- PreferenceManager.getDefaultSharedPreferences(mainActivity)
- .edit()
- .putString(
- PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS,
- new Gson().toJson(recentSearches))
- .apply();
+ searchResultsHintTV.setVisibility(View.VISIBLE);
- initRecentSearches(mainActivity);
+ ArrayList hybridFileParcelables = new ArrayList<>();
- return true;
- }
- return false;
+ boolean showHiddenFiles =
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .getBoolean(PREFERENCE_SHOW_HIDDENFILES, false);
+
+ ListFilesCommand.INSTANCE.listFiles(
+ mainActivity.getCurrentMainFragment().getPath(),
+ mainActivity.isRootExplorer(),
+ showHiddenFiles,
+ mode -> null,
+ hybridFileParcelable -> {
+ if (showHiddenFiles || !hybridFileParcelable.isHidden())
+ if (hybridFileParcelable
+ .getName(mainActivity)
+ .toLowerCase()
+ .contains(s.toLowerCase())) {
+ hybridFileParcelables.add(hybridFileParcelable);
+
+ searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+
+ searchRecyclerViewAdapter.notifyItemInserted(hybridFileParcelables.size() + 1);
+ }
+ return null;
});
+ }
- initSearchViewColor(mainActivity);
+ private void saveRecentPreference(String s) {
+
+ String preferenceString =
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .getString(PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS, null);
+
+ ArrayList recentSearches =
+ preferenceString != null
+ ? new Gson().fromJson(preferenceString, new TypeToken>() {}.getType())
+ : new ArrayList<>();
+
+ if (s.isEmpty() || recentSearches.contains(s)) return;
+
+ recentSearches.add(s);
+
+ if (recentSearches.size() > 5) recentSearches.remove(0);
+
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .edit()
+ .putString(
+ PreferencesConstants.PREFERENCE_RECENT_SEARCH_ITEMS, new Gson().toJson(recentSearches))
+ .apply();
+
+ initRecentSearches(mainActivity);
}
private void initRecentSearches(Context context) {
@@ -166,8 +222,10 @@ private void initRecentSearches(Context context) {
chip.setOnClickListener(
v -> {
- searchListener.onSearch(((Chip) v).getText().toString());
- appbar.getSearchView().hideSearchView();
+ String s = ((Chip) v).getText().toString();
+
+ searchViewEditText.setText(s);
+ search(s);
});
}
}
@@ -251,6 +309,8 @@ public void hideSearchView() {
animator = ObjectAnimator.ofFloat(searchViewLayout, "alpha", 1f, 0f);
}
+ clearRecyclerView();
+
// removing background fade view
mainActivity.hideSmokeScreen();
animator.setInterpolator(new AccelerateDecelerateInterpolator());
@@ -316,6 +376,14 @@ private void initSearchViewColor(MainActivity a) {
}
}
+ @SuppressLint("NotifyDataSetChanged")
+ private void clearRecyclerView() {
+ searchRecyclerViewAdapter.submitList(new ArrayList<>());
+ searchRecyclerViewAdapter.notifyDataSetChanged();
+
+ searchResultsHintTV.setVisibility(View.GONE);
+ }
+
public interface SearchListener {
void onSearch(String queue);
}
diff --git a/app/src/main/res/layout-v21/layout_search.xml b/app/src/main/res/layout-v21/layout_search.xml
index 3757da7b63..88222e41bf 100644
--- a/app/src/main/res/layout-v21/layout_search.xml
+++ b/app/src/main/res/layout-v21/layout_search.xml
@@ -1,97 +1,134 @@
-
-
+ tools:ignore="ContentDescription">
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+ android:layout_marginBottom="4dp"
+ android:text="Results"
+ android:textColor="@color/accent_material_light"
+ android:textSize="12sp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@id/searchRecyclerView"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/searchRecentItemsScrollView" />
-
+
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-w720dp/layout_search.xml b/app/src/main/res/layout-w720dp/layout_search.xml
index 0da790bdf4..791fad9dc0 100644
--- a/app/src/main/res/layout-w720dp/layout_search.xml
+++ b/app/src/main/res/layout-w720dp/layout_search.xml
@@ -1,94 +1,112 @@
-
-
-
-
+ tools:ignore="ContentDescription">
-
+
-
+
-
+
-
+ android:layout_marginBottom="4dp"
+ android:text="Recent"
+ android:textColor="@color/accent_material_light"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toTopOf="@id/searchRecentItemsChipGroup"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/search_edit_text" />
+
+
-
+
+
+
+
+
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_search.xml b/app/src/main/res/layout/layout_search.xml
index 04bc137d5e..a292e6af6b 100644
--- a/app/src/main/res/layout/layout_search.xml
+++ b/app/src/main/res/layout/layout_search.xml
@@ -1,94 +1,113 @@
-
-
-
-
+ tools:ignore="ContentDescription">
-
+
-
+
-
+
-
+ android:layout_marginBottom="4dp"
+ android:text="Recent"
+ android:textColor="@color/accent_material_light"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toTopOf="@id/searchRecentItemsChipGroup"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/search_edit_text" />
+
+
-
+
+
+
+
+
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/search_row_item.xml b/app/src/main/res/layout/search_row_item.xml
new file mode 100644
index 0000000000..9bfca31161
--- /dev/null
+++ b/app/src/main/res/layout/search_row_item.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From da13eb5a6253353a6b1cf3b00c7df6ba242498ad Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 3 May 2023 12:42:08 +0530
Subject: [PATCH 012/328] [WIP] simple search -- update UI
---
.../adapters/SearchRecyclerViewAdapter.kt | 27 ++++++++-------
.../ui/views/appbar/SearchView.java | 1 +
app/src/main/res/layout/search_row_item.xml | 34 ++-----------------
3 files changed, 17 insertions(+), 45 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
index b9e7eefae5..42b4f6a400 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -20,7 +20,7 @@
package com.amaze.filemanager.adapters
-import android.text.format.Formatter
+import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -30,8 +30,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.amaze.filemanager.R
import com.amaze.filemanager.filesystem.HybridFileParcelable
-import com.amaze.filemanager.utils.Utils
-import java.util.*
+import java.util.Random
class SearchRecyclerViewAdapter :
ListAdapter(
@@ -62,28 +61,30 @@ class SearchRecyclerViewAdapter :
val item = getItem(position)
holder.fileNameTV.text = item.name
-
- holder.dateTV.text = Utils.getDate(holder.itemView.context, item.date)
-
- if (!item.isDirectory) {
- holder.sizeTV.text = Formatter.formatFileSize(holder.itemView.context, item.size)
- }
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val fileNameTV: TextView
- val dateTV: TextView
- val sizeTV: TextView
init {
view.setOnClickListener {
}
+ view.findViewById(R.id.searchItemSampleColorView)
+ .setBackgroundColor(getRandomColor())
+
fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
- dateTV = view.findViewById(R.id.searchItemDateTV)
- sizeTV = view.findViewById(R.id.searchItemSizeTV)
+ }
+
+ private fun getRandomColor(): Int {
+ val colorArray = arrayOf(
+ "#e57373", "#f06292", "#ba68c8", "#9575cd", "#7986cb", "#64b5f6", "#4fc3f7",
+ "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f",
+ "#ffb74d", "#ff8a65", "#a1887f"
+ )
+ return Color.parseColor(colorArray[Random().nextInt(colorArray.size - 1)])
}
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 4285ac71c6..4d10bd43c7 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -146,6 +146,7 @@ private void search(String s) {
PreferenceManager.getDefaultSharedPreferences(mainActivity)
.getBoolean(PREFERENCE_SHOW_HIDDENFILES, false);
+ // TODO: takes too much resources & freezes main thread on huge folders
ListFilesCommand.INSTANCE.listFiles(
mainActivity.getCurrentMainFragment().getPath(),
mainActivity.isRootExplorer(),
diff --git a/app/src/main/res/layout/search_row_item.xml b/app/src/main/res/layout/search_row_item.xml
index 9bfca31161..2069032c96 100644
--- a/app/src/main/res/layout/search_row_item.xml
+++ b/app/src/main/res/layout/search_row_item.xml
@@ -27,43 +27,13 @@
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
- android:layout_marginBottom="2dp"
+ android:layout_marginBottom="8dp"
android:textSize="16sp"
- app:layout_constraintBottom_toTopOf="@id/searchItemSizeTV"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/searchItemSampleColorView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0" />
-
-
-
-
\ No newline at end of file
From b8f967c53f7b757ea7aeffc8b76901113a98b0e2 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 3 May 2023 22:55:26 +0530
Subject: [PATCH 013/328] simple search MVP
---
.../adapters/SearchRecyclerViewAdapter.kt | 43 ++++++++++++++-----
1 file changed, 32 insertions(+), 11 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
index 42b4f6a400..c3f233ceb5 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -20,16 +20,20 @@
package com.amaze.filemanager.adapters
-import android.graphics.Color
+import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.amaze.filemanager.R
+import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.filesystem.HybridFileParcelable
+import com.amaze.filemanager.ui.activities.MainActivity
+import com.amaze.filemanager.ui.colors.ColorPreference
import java.util.Random
class SearchRecyclerViewAdapter :
@@ -69,22 +73,39 @@ class SearchRecyclerViewAdapter :
init {
- view.setOnClickListener {
- }
+ fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
view.findViewById(R.id.searchItemSampleColorView)
- .setBackgroundColor(getRandomColor())
+ .setBackgroundColor(getRandomColor(view.context))
- fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
+ view.setOnClickListener {
+
+ val item = getItem(adapterPosition)
+
+ if (!item.isDirectory) {
+ item.openFile(
+ AppConfig.getInstance().mainActivityContext as MainActivity?,
+ false
+ )
+ } else {
+ (AppConfig.getInstance().mainActivityContext as MainActivity?)
+ ?.goToMain(item.path)
+ }
+
+ (AppConfig.getInstance().mainActivityContext as MainActivity?)
+ ?.appbar?.searchView?.hideSearchView()
+ }
}
- private fun getRandomColor(): Int {
- val colorArray = arrayOf(
- "#e57373", "#f06292", "#ba68c8", "#9575cd", "#7986cb", "#64b5f6", "#4fc3f7",
- "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f",
- "#ffb74d", "#ff8a65", "#a1887f"
+ private fun getRandomColor(context: Context): Int {
+ return ContextCompat.getColor(
+ context,
+ ColorPreference.availableColors[
+ Random().nextInt(
+ ColorPreference.availableColors.size - 1
+ )
+ ]
)
- return Color.parseColor(colorArray[Random().nextInt(colorArray.size - 1)])
}
}
}
From be5d436d93f4c6096eb0b5c811dc2479e4cec76e Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 14:08:19 +0530
Subject: [PATCH 014/328] indexed search MVP
---
.../ui/views/appbar/SearchView.java | 127 +++++++++++++++++-
app/src/main/res/layout-v21/layout_search.xml | 18 ++-
.../main/res/layout-w720dp/layout_search.xml | 18 ++-
app/src/main/res/layout/layout_search.xml | 18 ++-
app/src/main/res/values/strings.xml | 3 +
5 files changed, 175 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 4d10bd43c7..429b33e728 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -24,11 +24,14 @@
import static android.os.Build.VERSION.SDK_INT;
import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES;
+import java.io.File;
import java.util.ArrayList;
+import java.util.List;
import com.amaze.filemanager.R;
import com.amaze.filemanager.adapters.SearchRecyclerViewAdapter;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
+import com.amaze.filemanager.filesystem.RootHelper;
import com.amaze.filemanager.filesystem.root.ListFilesCommand;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
@@ -43,7 +46,11 @@
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.database.Cursor;
import android.graphics.PorterDuff;
+import android.provider.MediaStore;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -77,6 +84,7 @@ public class SearchView {
private final TextView recentHintTV;
private final TextView searchResultsHintTV;
+ private final TextView deepSearchTV;
private final ChipGroup recentChipGroup;
private final RecyclerView recyclerView;
@@ -84,8 +92,15 @@ public class SearchView {
private final SearchListener searchListener;
private final SearchRecyclerViewAdapter searchRecyclerViewAdapter;
+ // 0 -> Basic
+ // 1 -> Indexed
+ // 2 -> Recursive
+ private int searchMode;
+
private boolean enabled = false;
+ @SuppressWarnings("ConstantConditions")
+ @SuppressLint("NotifyDataSetChanged")
public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener searchListener) {
this.mainActivity = mainActivity;
@@ -99,8 +114,16 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
recentChipGroup = mainActivity.findViewById(R.id.searchRecentItemsChipGroup);
recentHintTV = mainActivity.findViewById(R.id.searchRecentHintTV);
searchResultsHintTV = mainActivity.findViewById(R.id.searchResultsHintTV);
+ deepSearchTV = mainActivity.findViewById(R.id.searchDeepSearchTV);
recyclerView = mainActivity.findViewById(R.id.searchRecyclerView);
+ searchMode = 0;
+ deepSearchTV.setText(
+ String.format(
+ "%s%s",
+ mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
+ mainActivity.getString(R.string.try_indexed_search)));
+
initRecentSearches(mainActivity);
searchRecyclerViewAdapter = new SearchRecyclerViewAdapter();
@@ -116,29 +139,86 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
backImageView.setOnClickListener(v -> appbar.getSearchView().hideSearchView());
+ searchViewEditText.addTextChangedListener(
+ new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ if (count > 0) searchViewEditText.setError(null);
+
+ if (count >= 3) onSearch(false);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ });
+
searchViewEditText.setOnEditorActionListener(
(v, actionId, event) -> {
- if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) return onSearch(true);
+
+ return false;
+ });
- String s = searchViewEditText.getText().toString().trim();
+ deepSearchTV.setOnClickListener(
+ v -> {
+ if (searchMode == 1) {
- search(s);
+ List hybridFileParcelables =
+ indexedSearch(mainActivity, searchViewEditText.getText().toString().trim());
+
+ searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+ searchRecyclerViewAdapter.notifyDataSetChanged();
+
+ searchMode = 2;
+ deepSearchTV.setText(
+ String.format(
+ "%s%s",
+ mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
+ mainActivity.getString(R.string.try_recursive_search)));
- saveRecentPreference(s);
+ } else if (searchMode == 2) {
- return true;
+ deepSearchTV.setVisibility(View.GONE);
}
- return false;
});
initSearchViewColor(mainActivity);
}
+ @SuppressWarnings("ConstantConditions")
+ private boolean onSearch(boolean shouldSave) {
+
+ String s = searchViewEditText.getText().toString().trim();
+
+ if (s.isEmpty()) {
+ searchViewEditText.setError(mainActivity.getString(R.string.field_empty));
+ searchViewEditText.requestFocus();
+ return false;
+ }
+
+ search(s);
+
+ if (shouldSave) saveRecentPreference(s);
+
+ return true;
+ }
+
private void search(String s) {
clearRecyclerView();
searchResultsHintTV.setVisibility(View.VISIBLE);
+ deepSearchTV.setVisibility(View.VISIBLE);
+ searchMode = 1;
+ deepSearchTV.setText(
+ String.format(
+ "%s%s",
+ mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
+ mainActivity.getString(R.string.try_indexed_search)));
ArrayList hybridFileParcelables = new ArrayList<>();
@@ -231,6 +311,41 @@ private void initRecentSearches(Context context) {
}
}
+ private List indexedSearch(MainActivity mainActivity, String query) {
+
+ ArrayList list = new ArrayList<>();
+ final String[] projection = {MediaStore.Files.FileColumns.DATA};
+
+ Cursor cursor =
+ mainActivity
+ .getContentResolver()
+ .query(MediaStore.Files.getContentUri("external"), projection, null, null, null);
+
+ if (cursor == null) return list;
+ else if (cursor.getCount() > 0 && cursor.moveToFirst()) {
+ do {
+ String path =
+ cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
+
+ if (path != null
+ && path.contains(mainActivity.getCurrentMainFragment().getPath())
+ && path.toLowerCase().contains(query.toLowerCase())) {
+
+ boolean showHiddenFiles =
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .getBoolean(PREFERENCE_SHOW_HIDDENFILES, false);
+
+ HybridFileParcelable hybridFileParcelable =
+ RootHelper.generateBaseFile(new File(path), showHiddenFiles);
+
+ if (hybridFileParcelable != null) list.add(hybridFileParcelable);
+ }
+ } while (cursor.moveToNext());
+ }
+ cursor.close();
+ return list;
+ }
+
/** show search view with a circular reveal animation */
public void revealSearchView() {
final int START_RADIUS = 16;
diff --git a/app/src/main/res/layout-v21/layout_search.xml b/app/src/main/res/layout-v21/layout_search.xml
index 88222e41bf..eda8994a46 100644
--- a/app/src/main/res/layout-v21/layout_search.xml
+++ b/app/src/main/res/layout-v21/layout_search.xml
@@ -125,10 +125,26 @@
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/searchDeepSearchTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchResultsHintTV" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-w720dp/layout_search.xml b/app/src/main/res/layout-w720dp/layout_search.xml
index 791fad9dc0..03e740801b 100644
--- a/app/src/main/res/layout-w720dp/layout_search.xml
+++ b/app/src/main/res/layout-w720dp/layout_search.xml
@@ -103,10 +103,26 @@
android:layout_width="@dimen/zero_dp"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/searchDeepSearchTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchRecentItemsScrollView" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_search.xml b/app/src/main/res/layout/layout_search.xml
index a292e6af6b..c81bcdc7f3 100644
--- a/app/src/main/res/layout/layout_search.xml
+++ b/app/src/main/res/layout/layout_search.xml
@@ -104,10 +104,26 @@
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/searchDeepSearchTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchRecentItemsScrollView" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9ef5b0be6f..24254559dc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -809,5 +809,8 @@ You only need to do this once, until the next time you select a new location for
No app found to handle this intent. Do you have DocumentsUI installed?
Cloud Connection credentials cleared
Unfortunately, we were unable to migrate your cloud connection credentials to the new database schema, and we had to remove them from the app. Please create the cloud connection again.
+ Not finding what you\'re looking for?
+ Try Recursive Search!
+ Try Indexed Search!
From eb2a7ac456138546fd1f39b11fa85cef26f52322 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 15:06:12 +0530
Subject: [PATCH 015/328] improve UX
---
.../ui/views/appbar/SearchView.java | 16 +++++++---
.../com/amaze/filemanager/utils/Utils.java | 8 +++++
app/src/main/res/layout-v21/layout_search.xml | 10 +++---
.../main/res/layout-w720dp/layout_search.xml | 30 ++++++++++++++----
app/src/main/res/layout/layout_search.xml | 31 ++++++++++++++-----
app/src/main/res/values/strings.xml | 2 ++
6 files changed, 75 insertions(+), 22 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 429b33e728..48751fe34c 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -120,7 +120,7 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
searchMode = 0;
deepSearchTV.setText(
String.format(
- "%s%s",
+ "%s %s",
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_indexed_search)));
@@ -158,7 +158,12 @@ public void afterTextChanged(Editable s) {}
searchViewEditText.setOnEditorActionListener(
(v, actionId, event) -> {
- if (actionId == EditorInfo.IME_ACTION_SEARCH) return onSearch(true);
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+
+ Utils.hideKeyboard(mainActivity);
+
+ return onSearch(true);
+ }
return false;
});
@@ -176,7 +181,7 @@ public void afterTextChanged(Editable s) {}
searchMode = 2;
deepSearchTV.setText(
String.format(
- "%s%s",
+ "%s %s",
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_recursive_search)));
@@ -216,7 +221,7 @@ private void search(String s) {
searchMode = 1;
deepSearchTV.setText(
String.format(
- "%s%s",
+ "%s %s",
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_indexed_search)));
@@ -306,6 +311,9 @@ private void initRecentSearches(Context context) {
String s = ((Chip) v).getText().toString();
searchViewEditText.setText(s);
+
+ Utils.hideKeyboard(mainActivity);
+
search(s);
});
}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/Utils.java b/app/src/main/java/com/amaze/filemanager/utils/Utils.java
index 009e25d58b..8c7f0d44be 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/Utils.java
+++ b/app/src/main/java/com/amaze/filemanager/utils/Utils.java
@@ -50,6 +50,7 @@
import android.text.format.DateUtils;
import android.util.DisplayMetrics;
import android.view.View;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
@@ -456,4 +457,11 @@ public static void addShortcut(
ShortcutManagerCompat.requestPinShortcut(context, info, null);
}
+
+ public static void hideKeyboard(MainActivity mainActivity) {
+ View view = mainActivity.getCurrentFocus();
+ if (view != null)
+ ((InputMethodManager) mainActivity.getSystemService(Context.INPUT_METHOD_SERVICE))
+ .hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
}
diff --git a/app/src/main/res/layout-v21/layout_search.xml b/app/src/main/res/layout-v21/layout_search.xml
index eda8994a46..ed8aaa8dcb 100644
--- a/app/src/main/res/layout-v21/layout_search.xml
+++ b/app/src/main/res/layout-v21/layout_search.xml
@@ -33,7 +33,7 @@
+
+
+ app:layout_constraintTop_toBottomOf="@id/searchResultsHintTV" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/searchResultsHintTV" />
Not finding what you\'re looking for?
Try Recursive Search!
Try Indexed Search!
+ Recent
+ Results
From f7dccf8b7c0280e006a06767d2aec31a14f3ad6b Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 15:12:34 +0530
Subject: [PATCH 016/328] recursive search MVP
---
.../com/amaze/filemanager/ui/views/appbar/SearchView.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 48751fe34c..4dbc7a7c93 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -170,10 +170,11 @@ public void afterTextChanged(Editable s) {}
deepSearchTV.setOnClickListener(
v -> {
+ String s = searchViewEditText.getText().toString().trim();
+
if (searchMode == 1) {
- List hybridFileParcelables =
- indexedSearch(mainActivity, searchViewEditText.getText().toString().trim());
+ List hybridFileParcelables = indexedSearch(mainActivity, s);
searchRecyclerViewAdapter.submitList(hybridFileParcelables);
searchRecyclerViewAdapter.notifyDataSetChanged();
@@ -187,6 +188,9 @@ public void afterTextChanged(Editable s) {}
} else if (searchMode == 2) {
+ searchListener.onSearch(s);
+ appbar.getSearchView().hideSearchView();
+
deepSearchTV.setVisibility(View.GONE);
}
});
From ce8bfbd13aee7bc288aec3b3eae2a8cf4a4a0867 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 15:33:16 +0530
Subject: [PATCH 017/328] UI
---
.../adapters/SearchRecyclerViewAdapter.kt | 35 ++++++++++++-------
.../ui/views/appbar/SearchView.java | 1 +
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
index c3f233ceb5..ba44316631 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -65,18 +65,27 @@ class SearchRecyclerViewAdapter :
val item = getItem(position)
holder.fileNameTV.text = item.name
+ holder.colorView.setBackgroundColor(getRandomColor(holder.colorView.context))
+
+// val colorPreference =
+// (AppConfig.getInstance().mainActivityContext as MainActivity).currentColorPreference
+//
+// if (item != null && item.isDirectory) { // always null for some reason!
+// holder.colorView.setBackgroundColor(colorPreference.iconSkin)
+// } else {
+// holder.colorView.setBackgroundColor(colorPreference.accent)
+// }
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val fileNameTV: TextView
+ val colorView: View
init {
fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
-
- view.findViewById(R.id.searchItemSampleColorView)
- .setBackgroundColor(getRandomColor(view.context))
+ colorView = view.findViewById(R.id.searchItemSampleColorView)
view.setOnClickListener {
@@ -96,16 +105,16 @@ class SearchRecyclerViewAdapter :
?.appbar?.searchView?.hideSearchView()
}
}
+ }
- private fun getRandomColor(context: Context): Int {
- return ContextCompat.getColor(
- context,
- ColorPreference.availableColors[
- Random().nextInt(
- ColorPreference.availableColors.size - 1
- )
- ]
- )
- }
+ private fun getRandomColor(context: Context): Int {
+ return ContextCompat.getColor(
+ context,
+ ColorPreference.availableColors[
+ Random().nextInt(
+ ColorPreference.availableColors.size - 1
+ )
+ ]
+ )
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 4dbc7a7c93..525f7a45ce 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -123,6 +123,7 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
"%s %s",
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_indexed_search)));
+ deepSearchTV.setVisibility(View.GONE);
initRecentSearches(mainActivity);
From 8c097529b42b53c42c0560a0c606da0bf1e6e23a Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 15:35:42 +0530
Subject: [PATCH 018/328] fix bug in indexed search
---
.../java/com/amaze/filemanager/ui/views/appbar/SearchView.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 525f7a45ce..3f71245056 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -342,7 +342,7 @@ else if (cursor.getCount() > 0 && cursor.moveToFirst()) {
if (path != null
&& path.contains(mainActivity.getCurrentMainFragment().getPath())
- && path.toLowerCase().contains(query.toLowerCase())) {
+ && new File(path).getName().toLowerCase().contains(query.toLowerCase())) {
boolean showHiddenFiles =
PreferenceManager.getDefaultSharedPreferences(mainActivity)
From 389dc54e2a60b96709845494453a5e687aafbc4c Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 15:47:15 +0530
Subject: [PATCH 019/328] add file path to results
---
.../adapters/SearchRecyclerViewAdapter.kt | 6 ++++-
app/src/main/res/layout/search_row_item.xml | 22 ++++++++++++++++---
2 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
index ba44316631..ad17c6caa9 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -65,12 +65,14 @@ class SearchRecyclerViewAdapter :
val item = getItem(position)
holder.fileNameTV.text = item.name
+ holder.filePathTV.text = item.path.substring(0, item.path.lastIndexOf("/"))
+
holder.colorView.setBackgroundColor(getRandomColor(holder.colorView.context))
// val colorPreference =
// (AppConfig.getInstance().mainActivityContext as MainActivity).currentColorPreference
//
-// if (item != null && item.isDirectory) { // always null for some reason!
+// if (item != null && item.isDirectory) { // always false for some reason!
// holder.colorView.setBackgroundColor(colorPreference.iconSkin)
// } else {
// holder.colorView.setBackgroundColor(colorPreference.accent)
@@ -80,11 +82,13 @@ class SearchRecyclerViewAdapter :
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val fileNameTV: TextView
+ val filePathTV: TextView
val colorView: View
init {
fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
+ filePathTV = view.findViewById(R.id.searchItemFilePathTV)
colorView = view.findViewById(R.id.searchItemSampleColorView)
view.setOnClickListener {
diff --git a/app/src/main/res/layout/search_row_item.xml b/app/src/main/res/layout/search_row_item.xml
index 2069032c96..0ff8072465 100644
--- a/app/src/main/res/layout/search_row_item.xml
+++ b/app/src/main/res/layout/search_row_item.xml
@@ -13,7 +13,7 @@
+
+
\ No newline at end of file
From 0e2b2373102db57ecf126cf5c6027b37549478f4 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 20:44:17 +0530
Subject: [PATCH 020/328] codacy: These nested if statements could be combined
---
.../filemanager/ui/views/appbar/SearchView.java | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 3f71245056..bdbeffe16a 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -243,17 +243,15 @@ private void search(String s) {
showHiddenFiles,
mode -> null,
hybridFileParcelable -> {
- if (showHiddenFiles || !hybridFileParcelable.isHidden())
- if (hybridFileParcelable
- .getName(mainActivity)
- .toLowerCase()
- .contains(s.toLowerCase())) {
- hybridFileParcelables.add(hybridFileParcelable);
+ if (hybridFileParcelable.getName(mainActivity).toLowerCase().contains(s.toLowerCase())
+ && (showHiddenFiles || !hybridFileParcelable.isHidden())) {
- searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+ hybridFileParcelables.add(hybridFileParcelable);
- searchRecyclerViewAdapter.notifyItemInserted(hybridFileParcelables.size() + 1);
- }
+ searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+
+ searchRecyclerViewAdapter.notifyItemInserted(hybridFileParcelables.size() + 1);
+ }
return null;
});
}
From 9db1d72eaedac11dae314135b84a87825bddcd2f Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 26 May 2023 20:45:35 +0530
Subject: [PATCH 021/328] codacy: Avoid unused private fields such as
'searchListener'.
---
.../java/com/amaze/filemanager/ui/views/appbar/SearchView.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index bdbeffe16a..f9a2b3e1a3 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -89,7 +89,6 @@ public class SearchView {
private final ChipGroup recentChipGroup;
private final RecyclerView recyclerView;
- private final SearchListener searchListener;
private final SearchRecyclerViewAdapter searchRecyclerViewAdapter;
// 0 -> Basic
@@ -104,7 +103,6 @@ public class SearchView {
public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener searchListener) {
this.mainActivity = mainActivity;
- this.searchListener = searchListener;
this.appbar = appbar;
searchViewLayout = mainActivity.findViewById(R.id.search_view);
From 5eeef32dbb9cf4854296943e85e2f0f7185dffc4 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sat, 24 Sep 2022 17:39:48 +0800
Subject: [PATCH 022/328] Subnetscanner v2
Fixes #2622 and fixes #3386.
---
app/build.gradle | 6 +-
app/src/main/AndroidManifest.xml | 3 +-
.../asynchronous/services/ftp/FtpService.kt | 110 +-----
.../services/ftp/FtpTileService.java | 5 +-
.../filemanager/filesystem/FileUtil.java | 2 +-
.../filemanager/filesystem/HybridFile.java | 2 +-
.../filesystem/ftp/NetCopyClientUtils.kt | 2 +-
.../ui/dialogs/SmbConnectDialog.java | 14 +-
.../ui/dialogs/SmbSearchDialog.java | 236 -------------
.../filemanager/ui/dialogs/SmbSearchDialog.kt | 256 ++++++++++++++
.../ui/fragments/CloudSheetFragment.java | 2 +-
.../ui/fragments/FtpServerFragment.kt | 6 +-
.../ui/notifications/FtpNotification.java | 3 +-
.../filemanager/utils/ComputerParcelable.kt | 7 +-
.../filemanager/utils/MainActivityHelper.java | 1 +
.../amaze/filemanager/utils/NetworkUtil.kt | 151 +++++++++
.../filemanager/utils/SubnetScanner.java | 252 --------------
.../com/amaze/filemanager/utils/UUIDv5.kt | 94 ++++++
.../smb/SameSubnetDiscoverDeviceStrategy.kt | 102 ++++++
.../utils/smb/SmbDeviceScannerObservable.kt | 96 ++++++
.../filemanager/utils/{ => smb}/SmbUtil.kt | 4 +-
.../utils/smb/WsddDiscoverDeviceStrategy.kt | 294 ++++++++++++++++
app/src/main/res/raw/wsd_request.txt | 22 ++
app/src/main/res/raw/wsdd_discovery.txt | 1 +
.../asynctasks/SmbDeleteTaskTest.kt | 2 +-
.../UtilitiesDatabaseMigrationTest.kt | 2 +-
.../filemanager/database/UtilsHandlerTest.kt | 2 +-
.../utils/ComputerParcelableTest.java | 6 +-
.../amaze/filemanager/utils/SmbUtilTest.kt | 8 +-
.../com/amaze/filemanager/utils/UUIDv5Test.kt | 43 +++
...tractSubnetDiscoverDevicesStrategyTests.kt | 75 +++++
.../SameSubnetDiscoverDevicesStrategyTest.kt | 58 ++++
.../WsddSubnetDiscoverDevicesStrategyTest.kt | 156 +++++++++
.../resources/wsdd/multicast-response.txt | 28 ++
app/src/test/resources/wsdd/wsd-response.txt | 46 +++
build.gradle | 4 +-
portscanner/.gitignore | 1 +
portscanner/build.gradle | 37 ++
portscanner/consumer-rules.pro | 0
portscanner/proguard-rules.pro | 21 ++
portscanner/src/main/AndroidManifest.xml | 8 +
.../com/stealthcopter/networktools/IPTools.kt | 130 ++++++++
.../stealthcopter/networktools/PortScan.kt | 315 ++++++++++++++++++
.../networktools/portscanning/PortScanTCP.kt | 50 +++
.../networktools/portscanning/PortScanUDP.kt | 57 ++++
settings.gradle | 1 +
.../filemanager/shadows/ShadowSmbUtil.kt | 2 +-
.../filemanager/test/volley/MockHttpStack.kt | 51 +++
48 files changed, 2141 insertions(+), 633 deletions(-)
delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.java
create mode 100644 app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
create mode 100644 app/src/main/java/com/amaze/filemanager/utils/NetworkUtil.kt
delete mode 100644 app/src/main/java/com/amaze/filemanager/utils/SubnetScanner.java
create mode 100644 app/src/main/java/com/amaze/filemanager/utils/UUIDv5.kt
create mode 100644 app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt
create mode 100644 app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt
rename app/src/main/java/com/amaze/filemanager/utils/{ => smb}/SmbUtil.kt (98%)
create mode 100644 app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt
create mode 100644 app/src/main/res/raw/wsd_request.txt
create mode 100644 app/src/main/res/raw/wsdd_discovery.txt
create mode 100644 app/src/test/java/com/amaze/filemanager/utils/UUIDv5Test.kt
create mode 100644 app/src/test/java/com/amaze/filemanager/utils/smb/AbstractSubnetDiscoverDevicesStrategyTests.kt
create mode 100644 app/src/test/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDevicesStrategyTest.kt
create mode 100644 app/src/test/java/com/amaze/filemanager/utils/smb/WsddSubnetDiscoverDevicesStrategyTest.kt
create mode 100644 app/src/test/resources/wsdd/multicast-response.txt
create mode 100644 app/src/test/resources/wsdd/wsd-response.txt
create mode 100644 portscanner/.gitignore
create mode 100644 portscanner/build.gradle
create mode 100644 portscanner/consumer-rules.pro
create mode 100644 portscanner/proguard-rules.pro
create mode 100644 portscanner/src/main/AndroidManifest.xml
create mode 100644 portscanner/src/main/java/com/stealthcopter/networktools/IPTools.kt
create mode 100644 portscanner/src/main/java/com/stealthcopter/networktools/PortScan.kt
create mode 100644 portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanTCP.kt
create mode 100644 portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanUDP.kt
create mode 100644 testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt
diff --git a/app/build.gradle b/app/build.gradle
index 34cd3e4b97..2e1f2660f0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -133,7 +133,6 @@ dependencies {
implementation "androidx.room:room-rxjava2:$roomVersion"
kapt "androidx.annotation:annotation:$androidXAnnotationVersion"
- implementation "androidx.preference:preference:$androidXPrefVersion"
implementation "androidx.preference:preference-ktx:$androidXPrefVersion"
//For tests
@@ -233,14 +232,15 @@ dependencies {
implementation 'org.tukaani:xz:1.9'
- implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
+ implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
// (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
- implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.2.9'
+ implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
implementation project(':commons_compress_7z')
implementation project(':file_operations')
+ implementation project(':portscanner')
implementation "androidx.core:core-ktx:$androidXCoreVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "ch.acra:acra-core:5.7.0"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3eb63f4c86..96ff20b8a3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,7 +54,8 @@
tools:replace="android:label"
android:label="@string/appbar_name"
android:requestLegacyExternalStorage="true"
- android:banner="@drawable/about_header">
+ android:banner="@drawable/about_header"
+ android:usesCleartextTraffic="true">
= M) {
- connected = cm.activeNetwork?.let { activeNetwork ->
- cm.getNetworkCapabilities(activeNetwork)?.let { ni ->
- ni.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) or
- ni.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
- } ?: false
- } ?: false
- } else {
- connected = cm.activeNetworkInfo?.let { ni ->
- ni.isConnected && (
- ni.type and (
- ConnectivityManager.TYPE_WIFI
- or ConnectivityManager.TYPE_ETHERNET
- ) != 0
- )
- } ?: false
- }
-
- if (!connected) {
- connected = runCatching {
- NetworkInterface.getNetworkInterfaces().toList().find { netInterface ->
- netInterface.displayName.startsWith("rndis") or
- netInterface.displayName.startsWith("wlan")
- }
- }.getOrElse { null } != null
- }
-
- return connected
- }
-
- /**
- * Is the device connected to Wifi?
- */
- @JvmStatic
- fun isConnectedToWifi(context: Context): Boolean {
- val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
- return if (SDK_INT >= M) {
- cm.activeNetwork?.let {
- cm.getNetworkCapabilities(it)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
- } ?: false
- } else {
- cm.activeNetworkInfo?.let {
- it.isConnected && it.type == ConnectivityManager.TYPE_WIFI
- } ?: false
- }
- }
-
- /**
- * Determine device's IP address
- */
- @JvmStatic
- fun getLocalInetAddress(context: Context): InetAddress? {
- if (!isConnectedToLocalNetwork(context)) {
- return null
- }
- if (isConnectedToWifi(context)) {
- val wm = context.applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
- val ipAddress = wm.connectionInfo.ipAddress
- return if (ipAddress == 0) null else intToInet(ipAddress)
- }
- runCatching {
- NetworkInterface.getNetworkInterfaces().iterator().forEach { netinterface ->
- netinterface.inetAddresses.iterator().forEach { address ->
- // this is the condition that sometimes gives problems
- if (!address.isLoopbackAddress &&
- !address.isLinkLocalAddress
- ) {
- return address
- }
- }
- }
- }.onFailure { e ->
- log.warn("failed to get local inet address", e)
- }
- return null
- }
-
- private fun intToInet(value: Int): InetAddress? {
- val bytes = ByteArray(4)
- for (i in 0..3) {
- bytes[i] = byteOfInt(value, i)
- }
- return try {
- InetAddress.getByAddress(bytes)
- } catch (e: UnknownHostException) {
- // This only happens if the byte array has a bad length
- null
- }
- }
-
- private fun byteOfInt(value: Int, which: Int): Byte {
- val shift = which * 8
- return (value shr shift).toByte()
- }
-
private fun getPort(preferences: SharedPreferences): Int {
- return preferences.getInt(PORT_PREFERENCE_KEY, DEFAULT_PORT)
+ return preferences.getInt(FtpService.PORT_PREFERENCE_KEY, FtpService.DEFAULT_PORT)
}
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/services/ftp/FtpTileService.java b/app/src/main/java/com/amaze/filemanager/asynchronous/services/ftp/FtpTileService.java
index c9c44158f8..01e74981b3 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/services/ftp/FtpTileService.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/services/ftp/FtpTileService.java
@@ -24,6 +24,7 @@
import org.greenrobot.eventbus.Subscribe;
import com.amaze.filemanager.R;
+import com.amaze.filemanager.utils.NetworkUtil;
import android.annotation.TargetApi;
import android.content.Intent;
@@ -64,8 +65,8 @@ public void onClick() {
.sendBroadcast(
new Intent(FtpService.ACTION_STOP_FTPSERVER).setPackage(getPackageName()));
} else {
- if (FtpService.isConnectedToWifi(getApplicationContext())
- || FtpService.isConnectedToLocalNetwork(getApplicationContext())) {
+ if (NetworkUtil.isConnectedToWifi(getApplicationContext())
+ || NetworkUtil.isConnectedToLocalNetwork(getApplicationContext())) {
Intent i = new Intent(FtpService.ACTION_START_FTPSERVER).setPackage(getPackageName());
i.putExtra(FtpService.TAG_STARTED_BY_TILE, true);
getApplicationContext().sendBroadcast(i);
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java
index cd4bdec12b..3d129a9fdc 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java
@@ -43,7 +43,7 @@
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.utils.DataUtils;
import com.amaze.filemanager.utils.OTGUtil;
-import com.amaze.filemanager.utils.SmbUtil;
+import com.amaze.filemanager.utils.smb.SmbUtil;
import com.cloudrail.si.interfaces.CloudStorage;
import android.content.ContentResolver;
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
index 8f55be3882..2f91894d0d 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
@@ -85,8 +85,8 @@
import com.amaze.filemanager.utils.Function;
import com.amaze.filemanager.utils.OTGUtil;
import com.amaze.filemanager.utils.OnFileFound;
-import com.amaze.filemanager.utils.SmbUtil;
import com.amaze.filemanager.utils.Utils;
+import com.amaze.filemanager.utils.smb.SmbUtil;
import com.cloudrail.si.interfaces.CloudStorage;
import com.cloudrail.si.types.SpaceAllocation;
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/ftp/NetCopyClientUtils.kt b/app/src/main/java/com/amaze/filemanager/filesystem/ftp/NetCopyClientUtils.kt
index 372bc5279f..3fedf1b0eb 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/ftp/NetCopyClientUtils.kt
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/ftp/NetCopyClientUtils.kt
@@ -38,7 +38,7 @@ import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.COLO
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.SLASH
import com.amaze.filemanager.filesystem.smb.CifsContexts.SMB_URI_PREFIX
import com.amaze.filemanager.filesystem.ssh.SFtpClientTemplate
-import com.amaze.filemanager.utils.SmbUtil
+import com.amaze.filemanager.utils.smb.SmbUtil
import com.amaze.filemanager.utils.urlEncoded
import io.reactivex.Maybe
import io.reactivex.Scheduler
diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java
index 50f30fd136..b05b08e259 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialog.java
@@ -25,7 +25,7 @@
import static com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.COLON;
import static com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.SLASH;
import static com.amaze.filemanager.filesystem.smb.CifsContexts.SMB_URI_PREFIX;
-import static com.amaze.filemanager.utils.SmbUtil.PARAM_DISABLE_IPC_SIGNING_CHECK;
+import static com.amaze.filemanager.utils.smb.SmbUtil.PARAM_DISABLE_IPC_SIGNING_CHECK;
import static java.net.URLDecoder.decode;
import static java.net.URLEncoder.encode;
@@ -49,8 +49,8 @@
import com.amaze.filemanager.utils.EditTextColorStateUtil;
import com.amaze.filemanager.utils.PasswordUtil;
import com.amaze.filemanager.utils.SimpleTextWatcher;
-import com.amaze.filemanager.utils.SmbUtil;
import com.amaze.filemanager.utils.Utils;
+import com.amaze.filemanager.utils.smb.SmbUtil;
import com.google.android.material.textfield.TextInputLayout;
import android.app.Dialog;
@@ -227,15 +227,15 @@ public void afterTextChanged(@NonNull Editable s) {
final AppCompatCheckBox chkSmbDisableIpcSignature = binding.chkSmbDisableIpcSignature;
TextView help = binding.wanthelp;
- EditTextColorStateUtil.setTint(context, conName, accentColor);
- EditTextColorStateUtil.setTint(context, user, accentColor);
- EditTextColorStateUtil.setTint(context, pass, accentColor);
+ EditTextColorStateUtil.setTint(getActivity(), conName, accentColor);
+ EditTextColorStateUtil.setTint(getActivity(), user, accentColor);
+ EditTextColorStateUtil.setTint(getActivity(), pass, accentColor);
- Utils.setTint(context, chkSmbAnonymous, accentColor);
+ Utils.setTint(getActivity(), chkSmbAnonymous, accentColor);
help.setOnClickListener(
v -> {
int accentColor1 = ((ThemedActivity) getActivity()).getAccent();
- GeneralDialogCreation.showSMBHelpDialog(context, accentColor1);
+ GeneralDialogCreation.showSMBHelpDialog(getActivity(), accentColor1);
});
chkSmbAnonymous.setOnClickListener(
diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.java
deleted file mode 100644
index ee0626834f..0000000000
--- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra ,
- * Emmanuel Messulam, Raymond Lai and Contributors.
- *
- * This file is part of Amaze File Manager.
- *
- * Amaze File Manager is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.amaze.filemanager.ui.dialogs;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.afollestad.materialdialogs.MaterialDialog;
-import com.amaze.filemanager.R;
-import com.amaze.filemanager.ui.activities.MainActivity;
-import com.amaze.filemanager.ui.activities.superclasses.BasicActivity;
-import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity;
-import com.amaze.filemanager.ui.provider.UtilitiesProvider;
-import com.amaze.filemanager.ui.theme.AppTheme;
-import com.amaze.filemanager.utils.ComputerParcelable;
-import com.amaze.filemanager.utils.SubnetScanner;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-/** Created by arpitkh996 on 16-01-2016 edited by Emmanuel Messulam */
-public class SmbSearchDialog extends DialogFragment {
- private UtilitiesProvider utilsProvider;
-
- private ListViewAdapter listViewAdapter;
- private ArrayList computers = new ArrayList<>();
- private int accentColor;
- private SubnetScanner subnetScanner;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- utilsProvider = ((BasicActivity) getActivity()).getUtilsProvider();
-
- accentColor = ((ThemedActivity) getActivity()).getAccent();
- }
-
- @Override
- public void dismiss() {
- super.dismiss();
- if (subnetScanner != null) subnetScanner.cancel(true);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity());
- builder.title(R.string.searching_devices);
- builder.negativeColor(accentColor);
- builder.negativeText(R.string.cancel);
- builder.onNegative(
- (dialog, which) -> {
- if (subnetScanner != null) subnetScanner.cancel(true);
- dismiss();
- });
- builder.onPositive(
- (dialog, which) -> {
- if (subnetScanner != null) subnetScanner.cancel(true);
- if (getActivity() != null && getActivity() instanceof MainActivity) {
- dismiss();
- MainActivity mainActivity = (MainActivity) getActivity();
- mainActivity.showSMBDialog("", "", false);
- }
- });
- builder.positiveText(R.string.use_custom_ip);
- builder.positiveColor(accentColor);
- computers.add(new ComputerParcelable("-1", "-1"));
- listViewAdapter = new ListViewAdapter(getActivity(), computers);
- subnetScanner = new SubnetScanner(getActivity());
- subnetScanner.setObserver(
- new SubnetScanner.ScanObserver() {
- @Override
- public void computerFound(final ComputerParcelable computer) {
- if (getActivity() != null)
- getActivity()
- .runOnUiThread(
- () -> {
- if (!computers.contains(computer)) computers.add(computer);
- listViewAdapter.notifyDataSetChanged();
- });
- }
-
- @Override
- public void searchFinished() {
- if (getActivity() != null) {
- getActivity()
- .runOnUiThread(
- () -> {
- if (computers.size() == 1) {
- dismiss();
- Toast.makeText(
- getActivity(),
- getString(R.string.no_device_found),
- Toast.LENGTH_SHORT)
- .show();
- MainActivity mainActivity = (MainActivity) getActivity();
- mainActivity.showSMBDialog("", "", false);
- return;
- }
- computers.remove(computers.size() - 1);
- listViewAdapter.notifyDataSetChanged();
- });
- }
- }
- });
- subnetScanner.execute();
-
- builder.adapter(listViewAdapter, null);
- return builder.build();
- }
-
- private class ListViewAdapter extends RecyclerView.Adapter {
- private static final int VIEW_PROGRESSBAR = 1;
- private static final int VIEW_ELEMENT = 2;
-
- private ArrayList items;
- private LayoutInflater mInflater;
-
- ListViewAdapter(Context context, List objects) {
- items = new ArrayList<>(objects);
- mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
- }
-
- @Override
- @NonNull
- public ElementViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view;
- switch (viewType) {
- case VIEW_PROGRESSBAR:
- view = mInflater.inflate(R.layout.smb_progress_row, parent, false);
- return new ElementViewHolder(view);
- default:
- case VIEW_ELEMENT:
- view = mInflater.inflate(R.layout.smb_computers_row, parent, false);
- return new ElementViewHolder(view);
- }
- }
-
- @Override
- public void onBindViewHolder(@NonNull ElementViewHolder holder, int position) {
- int viewType = getItemViewType(position);
- if (viewType == VIEW_PROGRESSBAR) {
- return;
- }
-
- ComputerParcelable f = items.get(position);
-
- holder.rootView.setOnClickListener(
- v -> {
- if (subnetScanner != null) subnetScanner.cancel(true);
- if (getActivity() != null && getActivity() instanceof MainActivity) {
- dismiss();
- MainActivity mainActivity = (MainActivity) getActivity();
- mainActivity.showSMBDialog(
- listViewAdapter.items.get(position).getName(),
- listViewAdapter.items.get(position).getAddr(),
- false);
- }
- });
-
- holder.txtTitle.setText(f.getName());
- holder.image.setImageResource(R.drawable.ic_settings_remote_white_48dp);
- if (utilsProvider.getAppTheme().equals(AppTheme.LIGHT))
- holder.image.setColorFilter(Color.parseColor("#666666"));
- holder.txtDesc.setText(f.getAddr());
- }
-
- @Override
- public int getItemViewType(int position) {
- ComputerParcelable f = items.get(position);
- if (f.getAddr().equals("-1")) {
- return VIEW_PROGRESSBAR;
- } else {
- return VIEW_ELEMENT;
- }
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public int getItemCount() {
- return items.size();
- }
- }
-
- private static class ElementViewHolder extends RecyclerView.ViewHolder {
- private View rootView;
-
- private ImageView image;
- private TextView txtTitle;
- private TextView txtDesc;
-
- ElementViewHolder(View view) {
- super(view);
-
- rootView = view;
-
- txtTitle = view.findViewById(R.id.firstline);
- image = view.findViewById(R.id.icon);
- txtDesc = view.findViewById(R.id.secondLine);
- }
- }
-}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
new file mode 100644
index 0000000000..bd92520db8
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.ui.dialogs
+
+import android.app.Activity
+import android.app.Dialog
+import android.content.Context
+import android.graphics.Color
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import android.widget.Toast
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.recyclerview.widget.RecyclerView
+import com.afollestad.materialdialogs.DialogAction
+import com.afollestad.materialdialogs.MaterialDialog
+import com.amaze.filemanager.R
+import com.amaze.filemanager.application.AppConfig
+import com.amaze.filemanager.ui.activities.MainActivity
+import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity
+import com.amaze.filemanager.ui.provider.UtilitiesProvider
+import com.amaze.filemanager.ui.theme.AppTheme
+import com.amaze.filemanager.utils.ComputerParcelable
+import com.amaze.filemanager.utils.smb.SmbDeviceScannerObservable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import org.slf4j.LoggerFactory
+
+/** Created by arpitkh996 on 16-01-2016 edited by Emmanuel Messulam @gmail.com> */
+class SmbSearchDialog : DialogFragment() {
+
+ private lateinit var utilsProvider: UtilitiesProvider
+ private lateinit var listViewAdapter: ListViewAdapter
+ private val viewModel = ComputerParcelableViewModel()
+ private var accentColor = 0
+ private lateinit var subnetScannerObserver: Disposable
+
+ override fun onCreate(bundle: Bundle?) {
+ super.onCreate(bundle)
+ utilsProvider = AppConfig.getInstance().utilsProvider
+ accentColor = (activity as ThemedActivity).accent
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ if (!subnetScannerObserver.isDisposed) {
+ subnetScannerObserver.dispose()
+ }
+ }
+
+ @Suppress("LongMethod", "LabeledExpression")
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val builder = MaterialDialog.Builder(requireActivity())
+ builder.title(R.string.searching_devices)
+ builder.negativeColor(accentColor)
+ builder.negativeText(R.string.cancel)
+ builder.onNegative { _: MaterialDialog?, _: DialogAction? ->
+ if (!subnetScannerObserver.isDisposed) {
+ subnetScannerObserver.dispose()
+ }
+ dismiss()
+ }
+ builder.onPositive { _: MaterialDialog?, _: DialogAction? ->
+ if (!subnetScannerObserver.isDisposed) {
+ subnetScannerObserver.dispose()
+ }
+ if (activity != null && activity is MainActivity) {
+ dismiss()
+ val mainActivity = activity as MainActivity
+ mainActivity.showSMBDialog("", "", false)
+ }
+ }
+ builder.positiveText(R.string.use_custom_ip)
+ builder.positiveColor(accentColor)
+ viewModel.valHolder.value = (ComputerParcelable("-1", "-1"))
+ listViewAdapter = ListViewAdapter(requireActivity())
+ val observable = SmbDeviceScannerObservable()
+ subnetScannerObserver = observable
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnDispose {
+ observable.stop()
+ }
+ .subscribe(
+ { computer: ComputerParcelable ->
+ if (!listViewAdapter.contains(computer)) {
+ viewModel.valHolder.value = computer
+ }
+ },
+ { err: Throwable ->
+ LOG.error("Error searching for devices", err)
+ }
+ ) {
+ subnetScannerObserver.dispose()
+ activity?.runOnUiThread {
+ if (listViewAdapter.dummyOnly()) {
+ dismiss()
+ Toast.makeText(
+ activity,
+ getString(R.string.no_device_found),
+ Toast.LENGTH_SHORT
+ ).show()
+ val mainActivity = activity as MainActivity
+ mainActivity.showSMBDialog("", "", false)
+ return@runOnUiThread
+ }
+ listViewAdapter.removeDummy()
+ }
+ }
+ builder.adapter(listViewAdapter, null)
+ viewModel.valHolder.observe(this) {
+ listViewAdapter.add(it)
+ }
+ return builder.build()
+ }
+
+ private inner class ListViewAdapter(
+ context: Context
+ ) : RecyclerView.Adapter() {
+ private val items: MutableList = ArrayList()
+ private val mInflater: LayoutInflater
+
+ init {
+ mInflater = context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ }
+
+ /**
+ * Called by [ComputerParcelableViewModel], add found computer to list view
+ */
+ fun add(computer: ComputerParcelable) {
+ items.add(computer)
+ notifyDataSetChanged()
+ }
+
+ /**
+ * Called by Observable when finish probing. If no other computers found, remove first
+ * (dummy) host
+ */
+ fun removeDummy() {
+ items.removeFirst()
+ notifyDataSetChanged()
+ }
+
+ /**
+ * Answers if the computer list contains given instance.
+ */
+ fun contains(computer: ComputerParcelable): Boolean {
+ return items.contains(computer)
+ }
+
+ /**
+ * Answers if the list is empty = only has the dummy [ComputerParcelable] instance
+ */
+ fun dummyOnly(): Boolean {
+ return items.size == 1 && items.first().addr == "-1"
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view: View
+ return when (viewType) {
+ VIEW_PROGRESSBAR -> {
+ view = mInflater.inflate(R.layout.smb_progress_row, parent, false)
+ ViewHolder(view)
+ }
+ else -> {
+ view =
+ mInflater.inflate(R.layout.smb_computers_row, parent, false)
+ ElementViewHolder(view)
+ }
+ }
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val viewType = getItemViewType(position)
+ if (viewType == Companion.VIEW_PROGRESSBAR) {
+ return
+ }
+ val (addr, name) = items[position]
+ holder.rootView.setOnClickListener {
+ if (activity != null && activity is MainActivity) {
+ dismiss()
+ val mainActivity = activity as MainActivity
+ mainActivity.showSMBDialog(
+ listViewAdapter.items[position].name,
+ listViewAdapter.items[position].addr,
+ false
+ )
+ }
+ }
+ if (holder is ElementViewHolder) {
+ holder.txtTitle.text = name
+ holder.image.setImageResource(R.drawable.ic_settings_remote_white_48dp)
+ if (utilsProvider.appTheme == AppTheme.LIGHT) {
+ holder.image.setColorFilter(Color.parseColor("#666666"))
+ }
+ holder.txtDesc.text = addr
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ val (addr) = items[position]
+ return if (addr == "-1") {
+ VIEW_PROGRESSBAR
+ } else {
+ VIEW_ELEMENT
+ }
+ }
+
+ override fun getItemId(position: Int): Long = position.toLong()
+
+ override fun getItemCount(): Int = items.size
+ }
+
+ private open class ViewHolder(val rootView: View) : RecyclerView.ViewHolder(rootView)
+
+ private class ElementViewHolder(rootView: View) :
+ ViewHolder(rootView) {
+ val image: ImageView = rootView.findViewById(R.id.icon)
+ val txtTitle: TextView = rootView.findViewById(R.id.firstline)
+ val txtDesc: TextView = rootView.findViewById(R.id.secondLine)
+ }
+
+ private class ComputerParcelableViewModel : ViewModel() {
+ val valHolder = MutableLiveData()
+ }
+
+ companion object {
+ private const val VIEW_PROGRESSBAR = 1
+ private const val VIEW_ELEMENT = 2
+ private val LOG = LoggerFactory.getLogger(SmbSearchDialog::class.java)
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/CloudSheetFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/CloudSheetFragment.java
index 648abc8b3c..0037e97a49 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/fragments/CloudSheetFragment.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/CloudSheetFragment.java
@@ -159,7 +159,7 @@ public void onClick(View v) {
case R.id.linear_layout_smb:
dismiss();
SmbSearchDialog smbDialog = new SmbSearchDialog();
- smbDialog.show(getActivity().getFragmentManager(), "tab");
+ smbDialog.show(getActivity().getSupportFragmentManager(), "tab");
return;
case R.id.linear_layout_scp:
dismiss();
diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/FtpServerFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/FtpServerFragment.kt
index 31545e7b88..9cde4e5cc7 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/fragments/FtpServerFragment.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/FtpServerFragment.kt
@@ -68,9 +68,6 @@ import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.asynchronous.services.ftp.FtpService
import com.amaze.filemanager.asynchronous.services.ftp.FtpService.Companion.KEY_PREFERENCE_PATH
import com.amaze.filemanager.asynchronous.services.ftp.FtpService.Companion.KEY_PREFERENCE_ROOT_FILESYSTEM
-import com.amaze.filemanager.asynchronous.services.ftp.FtpService.Companion.getLocalInetAddress
-import com.amaze.filemanager.asynchronous.services.ftp.FtpService.Companion.isConnectedToLocalNetwork
-import com.amaze.filemanager.asynchronous.services.ftp.FtpService.Companion.isConnectedToWifi
import com.amaze.filemanager.asynchronous.services.ftp.FtpService.Companion.isRunning
import com.amaze.filemanager.asynchronous.services.ftp.FtpService.FtpReceiverActions
import com.amaze.filemanager.databinding.DialogFtpLoginBinding
@@ -80,6 +77,9 @@ import com.amaze.filemanager.ui.activities.MainActivity
import com.amaze.filemanager.ui.notifications.FtpNotification
import com.amaze.filemanager.ui.runIfDocumentsUIExists
import com.amaze.filemanager.ui.theme.AppTheme
+import com.amaze.filemanager.utils.NetworkUtil.getLocalInetAddress
+import com.amaze.filemanager.utils.NetworkUtil.isConnectedToLocalNetwork
+import com.amaze.filemanager.utils.NetworkUtil.isConnectedToWifi
import com.amaze.filemanager.utils.OneCharacterCharSequence
import com.amaze.filemanager.utils.PasswordUtil
import com.amaze.filemanager.utils.Utils
diff --git a/app/src/main/java/com/amaze/filemanager/ui/notifications/FtpNotification.java b/app/src/main/java/com/amaze/filemanager/ui/notifications/FtpNotification.java
index d803267ff7..defb81ff26 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/notifications/FtpNotification.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/notifications/FtpNotification.java
@@ -28,6 +28,7 @@
import com.amaze.filemanager.R;
import com.amaze.filemanager.asynchronous.services.ftp.FtpService;
import com.amaze.filemanager.ui.activities.MainActivity;
+import com.amaze.filemanager.utils.NetworkUtil;
import android.app.Notification;
import android.app.NotificationManager;
@@ -104,7 +105,7 @@ public static void updateNotification(Context context, boolean noStopButton) {
boolean secureConnection =
sharedPreferences.getBoolean(FtpService.KEY_PREFERENCE_SECURE, FtpService.DEFAULT_SECURE);
- InetAddress address = FtpService.getLocalInetAddress(context);
+ InetAddress address = NetworkUtil.getLocalInetAddress(context);
String address_text = "Address not found";
diff --git a/app/src/main/java/com/amaze/filemanager/utils/ComputerParcelable.kt b/app/src/main/java/com/amaze/filemanager/utils/ComputerParcelable.kt
index b5358507f1..4b8a6eeac6 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/ComputerParcelable.kt
+++ b/app/src/main/java/com/amaze/filemanager/utils/ComputerParcelable.kt
@@ -24,6 +24,9 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
-data class ComputerParcelable(val addr: String?, val name: String?) : Parcelable {
- override fun toString(): String = String.format("%s [%s]", name, addr)
+data class ComputerParcelable(val addr: String, val name: String) : Parcelable {
+ override fun toString(): String = "$name [$addr]"
+ override fun hashCode(): Int = addr.hashCode()
+ override fun equals(other: Any?): Boolean =
+ other is ComputerParcelable && other.addr == this.addr
}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java b/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java
index 53a029c69e..9caba5adb9 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java
+++ b/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java
@@ -66,6 +66,7 @@
import com.amaze.filemanager.ui.fragments.TabFragment;
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
import com.amaze.filemanager.ui.views.WarnableTextInputValidator;
+import com.amaze.filemanager.utils.smb.SmbUtil;
import com.leinardi.android.speeddial.SpeedDialView;
import android.annotation.SuppressLint;
diff --git a/app/src/main/java/com/amaze/filemanager/utils/NetworkUtil.kt b/app/src/main/java/com/amaze/filemanager/utils/NetworkUtil.kt
new file mode 100644
index 0000000000..8bf5b4b4d6
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/utils/NetworkUtil.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils
+
+import android.app.Service
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.net.wifi.WifiManager
+import android.os.Build
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.net.InetAddress
+import java.net.NetworkInterface
+import java.net.UnknownHostException
+
+object NetworkUtil {
+
+ private val log: Logger = LoggerFactory.getLogger(NetworkUtil::class.java)
+
+ private fun getConnectivityManager(context: Context) =
+ context.applicationContext.getSystemService(Service.CONNECTIVITY_SERVICE)
+ as ConnectivityManager
+
+ /**
+ * Is the device connected to local network, either Ethernet or Wifi?
+ */
+ @JvmStatic
+ fun isConnectedToLocalNetwork(context: Context): Boolean {
+ val cm = getConnectivityManager(context)
+ var connected: Boolean
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ connected = cm.activeNetwork?.let { activeNetwork ->
+ cm.getNetworkCapabilities(activeNetwork)?.let { ni ->
+ ni.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) or
+ ni.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
+ } ?: false
+ } ?: false
+ } else {
+ connected = cm.activeNetworkInfo?.let { ni ->
+ ni.isConnected && (
+ ni.type and (
+ ConnectivityManager.TYPE_WIFI
+ or ConnectivityManager.TYPE_ETHERNET
+ ) != 0
+ )
+ } ?: false
+ }
+
+ if (!connected) {
+ connected = runCatching {
+ NetworkInterface.getNetworkInterfaces().toList().find { netInterface ->
+ netInterface.displayName.startsWith("rndis") or
+ netInterface.displayName.startsWith("wlan")
+ }
+ }.getOrElse { null } != null
+ }
+
+ return connected
+ }
+
+ /**
+ * Is the device connected to Wifi?
+ */
+ @JvmStatic
+ fun isConnectedToWifi(context: Context): Boolean {
+ val cm = getConnectivityManager(context)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ cm.activeNetwork?.let {
+ cm.getNetworkCapabilities(it)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ } ?: false
+ } else {
+ cm.activeNetworkInfo?.let {
+ it.isConnected && it.type == ConnectivityManager.TYPE_WIFI
+ } ?: false
+ }
+ }
+
+ /**
+ * Determine device's IP address.
+ *
+ * Caveat: doesn't handle IPv6 addresses well.
+ */
+ @JvmStatic
+ fun getLocalInetAddress(context: Context): InetAddress? {
+ if (!isConnectedToLocalNetwork(context)) {
+ return null
+ }
+ if (isConnectedToWifi(context)) {
+ val wm = context.applicationContext.getSystemService(Service.WIFI_SERVICE)
+ as WifiManager
+ val ipAddress = wm.connectionInfo.ipAddress
+ return if (ipAddress == 0) null else intToInet(ipAddress)
+ }
+ runCatching {
+ NetworkInterface.getNetworkInterfaces().iterator().forEach { netinterface ->
+ netinterface.inetAddresses.iterator().forEach { address ->
+ // this is the condition that sometimes gives problems
+ if (!address.isLoopbackAddress &&
+ !address.isLinkLocalAddress
+ ) {
+ return address
+ }
+ }
+ }
+ }.onFailure { e ->
+ log.warn("failed to get local inet address", e)
+ }
+ return null
+ }
+
+ /**
+ * Utility method to convert an IPv4 address in integer representation to [InetAddress].
+ */
+ @JvmStatic
+ fun intToInet(value: Int): InetAddress? {
+ val bytes = ByteArray(4)
+ for (i in 0..3) {
+ bytes[i] = byteOfInt(value, i)
+ }
+ return try {
+ InetAddress.getByAddress(bytes)
+ } catch (e: UnknownHostException) {
+ // This only happens if the byte array has a bad length
+ null
+ }
+ }
+
+ private fun byteOfInt(value: Int, which: Int): Byte {
+ val shift = which * 8
+ return (value shr shift).toByte()
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/SubnetScanner.java b/app/src/main/java/com/amaze/filemanager/utils/SubnetScanner.java
deleted file mode 100644
index 3f6abfbbf7..0000000000
--- a/app/src/main/java/com/amaze/filemanager/utils/SubnetScanner.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra ,
- * Emmanuel Messulam, Raymond Lai and Contributors.
- *
- * This file is part of Amaze File Manager.
- *
- * Amaze File Manager is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.amaze.filemanager.utils;
-
-/** Created by arpitkh996 on 16-01-2016. */
-import static com.amaze.filemanager.filesystem.smb.CifsContexts.SMB_URI_PREFIX;
-
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import android.content.Context;
-import android.net.wifi.WifiManager;
-import android.os.AsyncTask;
-import android.text.format.Formatter;
-
-import jcifs.Address;
-import jcifs.CIFSException;
-import jcifs.NetbiosAddress;
-import jcifs.context.SingletonContext;
-import jcifs.smb.SmbFile;
-
-public class SubnetScanner extends AsyncTask {
-
- private static final String TAG = SubnetScanner.class.getSimpleName();
- private static final int RETRY_COUNT = 5;
- private static boolean initialized = false;
-
- private Thread bdThread;
- private final Object mLock;
- private List mResults;
- private ScanObserver observer;
- private ExecutorService pool;
- private List> tasks;
- private Context context;
-
- public interface ScanObserver {
- void computerFound(ComputerParcelable computer);
-
- void searchFinished();
- }
-
- class Task implements Callable {
- String addr;
-
- Task(String str) {
- this.addr = str;
- }
-
- public ComputerParcelable call() {
- try {
- NetbiosAddress[] allByAddress =
- SingletonContext.getInstance().getNameServiceClient().getNbtAllByAddress(this.addr);
- if (allByAddress == null || allByAddress.length <= 0) {
- return new ComputerParcelable(null, this.addr);
- }
- return new ComputerParcelable(allByAddress[0].getHostName(), this.addr);
- } catch (UnknownHostException e) {
- return new ComputerParcelable(null, this.addr);
- }
- }
- }
-
- public static void init() {
- Properties props = new Properties();
- props.setProperty("jcifs.resolveOrder", "BCAST");
- props.setProperty("jcifs.smb.client.responseTimeout", "30000");
- props.setProperty("jcifs.netbios.retryTimeout", "5000");
- props.setProperty("jcifs.netbios.cachePolicy", "-1");
- try {
- SingletonContext.init(props);
- initialized = true;
- } catch (CIFSException e) {
- android.util.Log.e(TAG, "Error initializing jcifs", e);
- }
- }
-
- public SubnetScanner(Context context) {
- this.context = context;
- mLock = new Object();
- tasks = new ArrayList<>(260);
- pool = Executors.newFixedThreadPool(60);
- mResults = new ArrayList<>();
- }
-
- @Override
- protected Void doInBackground(Void... voids) {
-
- if (!initialized) init();
-
- int ipAddress =
- ((WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE))
- .getConnectionInfo()
- .getIpAddress();
- if (ipAddress != 0) {
- tryWithBroadcast();
- String formatIpAddress = Formatter.formatIpAddress(ipAddress);
- String substring = formatIpAddress.substring(0, formatIpAddress.lastIndexOf(46) + 1);
- if (!isCancelled()) {
- for (ipAddress = 0; ipAddress < 100; ipAddress++) {
- this.tasks.add(this.pool.submit(new Task(substring + ipAddress)));
- this.tasks.add(this.pool.submit(new Task(substring + (ipAddress + 100))));
- if (ipAddress < 56) {
- this.tasks.add(this.pool.submit(new Task(substring + (ipAddress + 200))));
- }
- }
- while (!this.tasks.isEmpty()) {
- int size = this.tasks.size();
- int i = 0;
- while (i < size) {
- if (!isCancelled()) {
- try {
- ComputerParcelable computer =
- (ComputerParcelable) ((Future) this.tasks.get(i)).get(1, TimeUnit.MILLISECONDS);
- this.tasks.remove(i);
- size--;
- if (computer.getName() != null) {
- publishProgress(computer);
- }
- ipAddress = size;
- } catch (InterruptedException e) {
- return null;
- } catch (ExecutionException e2) {
- ipAddress = size;
- } catch (TimeoutException e3) {
- ipAddress = size;
- }
- i++;
- size = ipAddress;
- } else {
- return null;
- }
- }
- }
- try {
- this.bdThread.join();
- } catch (InterruptedException e4) {
- }
- } else {
- return null;
- }
- }
- synchronized (this.mLock) {
- if (this.observer != null) {
- this.observer.searchFinished();
- }
- }
-
- return null;
- }
-
- private void tryWithBroadcast() {
- this.bdThread =
- new Thread() {
- public void run() {
- for (int i = 0; i < SubnetScanner.RETRY_COUNT; i++) {
- try {
- SmbFile smbFile = SmbUtil.create(SMB_URI_PREFIX);
- smbFile.setConnectTimeout(5000);
- SmbFile[] listFiles = smbFile.listFiles();
- for (SmbFile smbFile2 : listFiles) {
- SmbFile[] listFiles2 = smbFile2.listFiles();
- for (SmbFile files : listFiles2) {
- try {
- String substring = files.getName().substring(0, files.getName().length() - 1);
- Address byName =
- SingletonContext.getInstance()
- .getNameServiceClient()
- .getByName(substring);
- if (byName != null) {
- publishProgress(new ComputerParcelable(substring, byName.getHostAddress()));
- }
- } catch (Throwable e) {
-
- }
- }
- }
- } catch (Throwable e2) {
-
- }
- }
- }
- };
- this.bdThread.start();
- }
-
- @Override
- protected void onPreExecute() {}
-
- @Override
- protected void onPostExecute(Void aVoid) {
- this.pool.shutdown();
- }
-
- @Override
- protected void onProgressUpdate(ComputerParcelable... computers) {
- for (ComputerParcelable computer : computers) {
- mResults.add(computer);
- synchronized (this.mLock) {
- if (this.observer != null) {
- this.observer.computerFound(computer);
- }
- }
- }
- }
-
- public void setObserver(ScanObserver scanObserver) {
- synchronized (this.mLock) {
- this.observer = scanObserver;
- }
- }
-
- @Override
- protected void onCancelled(Void aVoid) {
- super.onCancelled(aVoid);
- try {
- this.pool.shutdownNow();
- } catch (Throwable th) {
-
- }
- }
-
- public List getResults() {
- return new ArrayList<>(this.mResults);
- }
-}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/UUIDv5.kt b/app/src/main/java/com/amaze/filemanager/utils/UUIDv5.kt
new file mode 100644
index 0000000000..344482d034
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/utils/UUIDv5.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils
+
+import java.security.MessageDigest
+import java.security.NoSuchAlgorithmException
+import java.util.*
+
+/**
+ * UUIDv5 implementation, referenced from
+ * https://gist.github.com/icedraco/00118b4d3c91d96d8c58e837a448f1b8
+ */
+object UUIDv5 {
+
+ // Constants defined in RFC4122 https://www.ietf.org/rfc/rfc4122.txt
+ @JvmStatic
+ val DNS: UUID = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
+
+ @JvmStatic
+ val URL: UUID = UUID.fromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
+
+ @JvmStatic
+ val OID: UUID = UUID.fromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
+
+ @JvmStatic
+ val X500: UUID = UUID.fromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
+
+ /**
+ * Generate an UUIDv5 UUID from given namespace UUID and name.
+ *
+ * [namespaceUUID] must be one of [DNS], [URL], [OID], [X500].
+ */
+ @JvmStatic
+ @Suppress("TooGenericExceptionThrown")
+ fun fromString(namespaceUUID: UUID, name: String): UUID {
+ val md: MessageDigest
+ try {
+ md = MessageDigest.getInstance("SHA-1")
+ } catch (ex: NoSuchAlgorithmException) {
+ throw Exception("SHA-1 not supported", ex)
+ }
+
+ md.update(toBytes(namespaceUUID))
+ md.update(name.toByteArray())
+ val bytes = md.digest()
+ /* clear version; set to version 5 */
+ bytes[6] = ((bytes[6].toInt() and 0x0F) or 0x50).toByte()
+ /* clear variant; set to IETF variant */
+ bytes[8] = ((bytes[8].toInt() and 0x3F) or 0x80).toByte()
+ return fromBytes(bytes)
+ }
+
+ private fun fromBytes(data: ByteArray): UUID {
+ // Based on the private UUID(bytes[]) constructor
+ assert(data.size >= 16)
+ var msb = 0L
+ var lsb = 0L
+ for (i in 0..7)
+ msb = msb shl 8 or (data[i].toLong() and 0xff)
+ for (i in 8..15)
+ lsb = lsb shl 8 or (data[i].toLong() and 0xff)
+ return UUID(msb, lsb)
+ }
+
+ private fun toBytes(uuid: UUID): ByteArray {
+ // inverted logic of fromBytes()
+ val out = ByteArray(16)
+ val msb = uuid.mostSignificantBits
+ val lsb = uuid.leastSignificantBits
+ for (i in 0..7)
+ out[i] = (msb shr (7 - i) * 8 and 0xff).toByte()
+ for (i in 8..15)
+ out[i] = (lsb shr (15 - i) * 8 and 0xff).toByte()
+ return out
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt
new file mode 100644
index 0000000000..277150c647
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils.smb
+
+import com.amaze.filemanager.application.AppConfig
+import com.amaze.filemanager.utils.ComputerParcelable
+import com.amaze.filemanager.utils.NetworkUtil
+import com.stealthcopter.networktools.PortScan
+import io.reactivex.Flowable
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import java.net.Inet6Address
+import java.net.InetAddress
+
+/**
+ * [SmbDeviceScannerObservable.DiscoverDeviceStrategy] to just loop through other addresses within
+ * same subnet (/24 netmask) and knock their SMB service ports for reachability.
+ *
+ * Will bypass [Inet6Address] device addresses. They may have much bigger neighourhood host count;
+ * also for devices using IPv6, they shall be covered by [WsddDiscoverDeviceStrategy] anyway.
+ *
+ * TODO: if we can get the gateway using __legit__ API, may swarm the network in broader netmasks
+ */
+class SameSubnetDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStrategy {
+
+ private lateinit var worker: Disposable
+
+ companion object {
+ private const val HOST_UP_TIMEOUT = 1000
+ private const val PARALLELISM = 10
+ private val TCP_PORTS = arrayListOf(139, 445)
+ }
+
+ /**
+ * No need to cleanup resources
+ */
+ override fun onCancel() {
+ if (!worker.isDisposed) {
+ worker.dispose()
+ }
+ }
+
+ override fun discoverDevices(callback: (ComputerParcelable) -> Unit) {
+ val neighbourhoods = getNeighbourhoodHosts()
+ worker = Flowable.fromIterable(neighbourhoods)
+ .parallel(PARALLELISM)
+ .runOn(Schedulers.io())
+ .map { addr ->
+ if (addr.isReachable(HOST_UP_TIMEOUT)) {
+ val portsReachable = listOf(
+ PortScan.onAddress(addr).setPorts(TCP_PORTS).setMethodTCP().doScan()
+ ).flatten()
+ if (portsReachable.isNotEmpty()) {
+ addr
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }.filter {
+ it is InetAddress
+ }.doOnNext { addr ->
+ addr as InetAddress
+ callback.invoke(ComputerParcelable(addr.hostAddress, addr.hostName))
+ }.sequential().subscribe()
+ }
+
+ private fun getNeighbourhoodHosts(): List {
+ val deviceAddress = NetworkUtil.getLocalInetAddress(AppConfig.getInstance())
+ return deviceAddress?.let { addr ->
+ if (addr is Inet6Address) {
+ // IPv6 neigbourhood hosts can be very big - that should use wsdd instead; hence
+ // empty list here
+ emptyList()
+ } else {
+ val networkPrefix: String = addr.hostAddress.substringBeforeLast('.')
+ (1..254).map {
+ InetAddress.getByName("$networkPrefix.$it")
+ }
+ }
+ } ?: emptyList()
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt
new file mode 100644
index 0000000000..14dcced9d8
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils.smb
+
+import com.amaze.filemanager.utils.ComputerParcelable
+import com.amaze.filemanager.utils.smb.SmbDeviceScannerObservable.DiscoverDeviceStrategy
+import io.reactivex.Observable
+import io.reactivex.Observer
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import java.net.InetAddress
+
+/**
+ * Observable to discover reachable SMB nodes on the network.
+ *
+ * Uses a series of [DiscoverDeviceStrategy] instances to discover nodes.
+ */
+class SmbDeviceScannerObservable : Observable() {
+
+ /**
+ * Device discovery strategy interface.
+ */
+ interface DiscoverDeviceStrategy {
+ /**
+ * Implement this method to return list of [InetAddress] which has SMB service running.
+ */
+ fun discoverDevices(callback: (ComputerParcelable) -> Unit)
+
+ /**
+ * Implement this method to cleanup resources
+ */
+ fun onCancel()
+ }
+
+ private var discoverDeviceStrategies: Array =
+ arrayOf(
+ WsddDiscoverDeviceStrategy(),
+ SameSubnetDiscoverDeviceStrategy()
+ )
+
+ private lateinit var observer: Observer
+
+ private lateinit var disposable: Disposable
+
+ /**
+ * Stop discovering hosts. Notify containing strategies to stop, then stop the created
+ * [Observer] obtained at [subscribeActual].
+ */
+ fun stop() {
+ if (!disposable.isDisposed) {
+ disposable.dispose()
+ }
+ observer.onComplete()
+ }
+
+ /**
+ * Call all strategies one by one to discover nodes.
+ *
+ * Given observer must be able to drop duplicated entries (which ComputerParcelable already
+ * has implemented equals() and hashCode()).
+ */
+ override fun subscribeActual(observer: Observer) {
+ this.observer = observer
+ this.disposable = merge(
+ discoverDeviceStrategies.map { strategy ->
+ fromCallable {
+ strategy.discoverDevices { addr ->
+ observer.onNext(ComputerParcelable(addr.addr, addr.name))
+ }
+ }.subscribeOn(Schedulers.io())
+ }
+ ).observeOn(Schedulers.computation()).doOnComplete {
+ discoverDeviceStrategies.forEach { strategy ->
+ strategy.onCancel()
+ }
+ }.subscribe()
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/SmbUtil.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/SmbUtil.kt
similarity index 98%
rename from app/src/main/java/com/amaze/filemanager/utils/SmbUtil.kt
rename to app/src/main/java/com/amaze/filemanager/utils/smb/SmbUtil.kt
index 35ae7bd5b9..eca1603f92 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/SmbUtil.kt
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/SmbUtil.kt
@@ -18,7 +18,7 @@
* along with this program. If not, see .
*/
-package com.amaze.filemanager.utils
+package com.amaze.filemanager.utils.smb
import android.content.Context
import android.net.Uri
@@ -30,6 +30,8 @@ import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.AT
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.COLON
import com.amaze.filemanager.filesystem.smb.CifsContexts.createWithDisableIpcSigningCheck
+import com.amaze.filemanager.utils.PasswordUtil
+import com.amaze.filemanager.utils.urlDecoded
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import jcifs.smb.NtlmPasswordAuthenticator
diff --git a/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt
new file mode 100644
index 0000000000..33e1813c69
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils.smb
+
+import androidx.annotation.VisibleForTesting
+import com.amaze.filemanager.R
+import com.amaze.filemanager.application.AppConfig
+import com.amaze.filemanager.utils.ComputerParcelable
+import com.amaze.filemanager.utils.NetworkUtil
+import com.android.volley.Response.ErrorListener
+import com.android.volley.VolleyError
+import com.android.volley.toolbox.StringRequest
+import com.android.volley.toolbox.Volley
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+import org.xmlpull.v1.XmlPullParserFactory
+import java.io.StringReader
+import java.net.DatagramPacket
+import java.net.InetAddress
+import java.net.MulticastSocket
+import java.util.*
+
+/**
+ * [SmbDeviceScannerObservable.DiscoverDeviceStrategy] implementation to discover SMB devices using
+ * [Web service discovery](https://en.wikipedia.org/wiki/WS-Discovery), which is used by SMBv2 or
+ * above.
+ *
+ * Discovery method goes this way:
+ * 1. send a SOAP request to multicast address 239.255.255.250 port 3702 over UDP
+ * 2. for each reply as SOAP XML too, extract their URN and record the address the packets are from
+ * 3. if the reply indicates sender is a computer, send a HTTP POST to the address recorded in 2, port 5357
+ * 4. verify result and send [ComputerParcelable] in callback
+ *
+ * Implementation is after reference: https://fitzcarraldoblog.wordpress.com/2020/07/08/a-linux-command-line-utility-to-discover-and-list-wsd-enabled-computers-and-printers-on-a-home-network/
+ * (Python though).
+ *
+ * Original implementation calls for UUIDv5 which will use hash value of the device's MAC address;
+ * this implementation is not using, since MAC address poses privacy concern, and newer Androids are
+ * making difficult to fetch MAC addresses anyway.
+ *
+ * Manually setting [multicastSocketFactory] Allows customized method to be specified for creating [MulticastSocket]
+ * for convenience of testing.
+ *
+ * @author TranceLove
+ */
+class WsddDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStrategy {
+
+ private val multicastRequestTemplate = AppConfig.getInstance()
+ .resources.openRawResource(R.raw.wsdd_discovery)
+ .reader(Charsets.UTF_8).readText()
+
+ private val wsdRequestTemplate = AppConfig.getInstance()
+ .resources.openRawResource(R.raw.wsd_request)
+ .reader(Charsets.UTF_8).readText()
+
+ private val wsdRequestHeaders = mutableMapOf(
+ Pair("Accept-Encoding", "Identity"),
+ Pair("Content-Type", "application/soap+xml"),
+ Pair("Connection", "Close"),
+ Pair("User-Agent", "wsd")
+ )
+
+ var multicastSocketFactory: () -> MulticastSocket = DEFAULT_MULTICAST_SOCKET_FACTORY
+ @VisibleForTesting
+ get
+
+ @VisibleForTesting
+ set
+
+ private val queue = Volley.newRequestQueue(AppConfig.getInstance())
+
+ private var cancelled = false
+
+ init {
+ queue.start()
+ }
+
+ override fun discoverDevices(callback: (ComputerParcelable) -> Unit) {
+ multicastForDevice { addr ->
+ callback.invoke(addr)
+ }
+ }
+
+ @Suppress("LabeledExpression")
+ private fun multicastForDevice(callback: (ComputerParcelable) -> Unit) {
+ NetworkUtil.getLocalInetAddress(AppConfig.getInstance())?.let { addr ->
+ val multicastAddressV4 = InetAddress.getByName(BROADCAST_IPV4)
+ val multicastAddressV6 = InetAddress.getByName(BROADCAST_IPV6_LINK_LOCAL)
+
+ while (!cancelled) {
+ val socket: MulticastSocket = multicastSocketFactory.invoke()
+ socket.timeToLive = 1
+ socket.soTimeout = SOCKET_RECEIVE_TIMEOUT
+ socket.reuseAddress = true
+ socket.joinGroup(multicastAddressV4)
+ socket.joinGroup(multicastAddressV6)
+
+ // Specification said UUIDv5 which is device dependent. But random-based UUID should
+ // also work here
+ val tempDeviceUuid = UUID.randomUUID()
+ val request = multicastRequestTemplate
+ .replace("##MY_UUID##", tempDeviceUuid.toString())
+ .toByteArray(Charsets.UTF_8)
+
+ val requestPacket = DatagramPacket(
+ request,
+ request.size,
+ multicastAddressV4,
+ UDP_PORT
+ )
+ socket.send(requestPacket)
+
+ runCatching {
+ while (!socket.isClosed) {
+ val buffer = ByteArray(4096)
+ val replyPacket = DatagramPacket(buffer, buffer.size)
+ socket.receive(replyPacket)
+ if (replyPacket.data.isNotEmpty() && replyPacket.address != null) {
+ val sentFromAddress = replyPacket.address
+ queryWithResponseAsNecessary(
+ sentFromAddress,
+ tempDeviceUuid.toString(),
+ replyPacket.data,
+ callback
+ )
+ }
+ }
+ }.onFailure {
+ if (log.isWarnEnabled) log.warn("Error receiving reply", it)
+ socket.close()
+ }
+ }
+ }
+ }
+
+ private fun queryWithResponseAsNecessary(
+ sourceAddress: InetAddress,
+ tempDeviceId: String,
+ response: ByteArray,
+ callback: (ComputerParcelable) -> Unit
+ ) {
+ val values = parseXmlForResponse(response, arrayOf(WSD_TYPES, WSA_ADDRESS))
+ val type = values[WSD_TYPES]
+ val urn = values[WSA_ADDRESS]
+
+ if (true == type?.isNotEmpty() && true == urn?.isNotEmpty()) {
+ queryEndpointForResponse(type, sourceAddress, urn, tempDeviceId, callback)
+ }
+ }
+
+ private fun queryEndpointForResponse(
+ type: String,
+ sourceAddress: InetAddress,
+ urn: String,
+ tempDeviceId: String,
+ callback: (ComputerParcelable) -> Unit
+ ) {
+ if (type.endsWith(PUB_COMPUTER)) {
+ val messageId = UUID.randomUUID().toString()
+
+ val endpoint = urn.substringAfter(URN_UUID)
+ val dest =
+ "http://${sourceAddress.hostAddress}:$TCP_PORT/$endpoint"
+ queue.add(
+ object : StringRequest(
+ Method.POST,
+ dest,
+ { resp ->
+ if (log.isTraceEnabled) log.trace("Response: $resp")
+ val values = parseXmlForResponse(
+ resp,
+ arrayOf(WSDP_TYPES, WSA_ADDRESS, PUB_COMPUTER)
+ )
+ if (PUB_COMPUTER == values[WSDP_TYPES] && urn == values[WSA_ADDRESS]) {
+ if (true == values[PUB_COMPUTER]?.isNotEmpty()) {
+ val computerName: String = values[PUB_COMPUTER].let {
+ if (it!!.contains('/')) {
+ it.substringBefore("/")
+ } else {
+ it
+ }
+ }
+ callback(
+ ComputerParcelable(sourceAddress.hostAddress, computerName)
+ )
+ }
+ }
+ },
+ object : ErrorListener {
+ override fun onErrorResponse(error: VolleyError?) {
+ log.error("Error querying endpoint", error)
+ }
+ }
+ ) {
+ override fun getBody(): ByteArray {
+ return wsdRequestTemplate
+ .replace("##MESSAGE_ID##", "$URN_UUID$messageId")
+ .replace("##DEST_UUID##", urn)
+ .replace("##MY_UUID##", "$URN_UUID$tempDeviceId")
+ .toByteArray(Charsets.UTF_8)
+ }
+ override fun getHeaders(): MutableMap = wsdRequestHeaders
+ }
+ )
+ }
+ }
+
+ override fun onCancel() {
+ cancelled = true
+ queue.stop()
+ }
+
+ private fun parseXmlForResponse(xml: ByteArray, tags: Array) =
+ parseXmlForResponse(xml.toString(Charsets.UTF_8), tags)
+
+ private fun parseXmlForResponse(xml: String, tags: Array): Map {
+ if (xml.isEmpty()) {
+ return emptyMap()
+ } else {
+ val xmlParser = XmlPullParserFactory.newInstance().also {
+ it.isNamespaceAware = false
+ it.isValidating = false
+ }.newPullParser().also {
+ it.setInput(StringReader(xml))
+ }
+ val retval = WeakHashMap()
+ var currentTag: String = ""
+ var currentValue: String = ""
+ var event = xmlParser.eventType
+ try {
+ while (event != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ currentTag = xmlParser.name
+ } else if (event == XmlPullParser.TEXT) {
+ currentValue = xmlParser.text
+ } else if (event == XmlPullParser.END_TAG) {
+ if (tags.contains(currentTag)) {
+ retval[currentTag] = currentValue
+ currentTag = ""
+ currentValue = ""
+ }
+ }
+ event = xmlParser.next()
+ }
+ } catch (parseError: XmlPullParserException) {
+ log.warn("Error parsing XML", parseError)
+ // Combination of parsed result is required, hence it's all or nothing situation -
+ // if one error found, whole XML will not be valid. Clear for "no result" answer
+ retval.clear()
+ }
+ return retval
+ }
+ }
+
+ companion object {
+ private const val BROADCAST_IPV4 = "239.255.255.250"
+ private const val BROADCAST_IPV6_LINK_LOCAL = "[FF02::C]"
+ private const val UDP_PORT = 3702
+ private const val TCP_PORT = 5357
+ private const val SOCKET_RECEIVE_TIMEOUT = 60000 // 1 minute receive timeout
+
+ private const val URN_UUID = "urn:uuid:"
+ private const val WSA_ADDRESS = "wsa:Address"
+ private const val WSD_TYPES = "wsd:Types"
+ private const val WSDP_TYPES = "wsdp:Types"
+ private const val PUB_COMPUTER = "pub:Computer"
+
+ private val log: Logger = LoggerFactory.getLogger(WsddDiscoverDeviceStrategy::class.java)
+
+ private val DEFAULT_MULTICAST_SOCKET_FACTORY: () -> MulticastSocket = {
+ MulticastSocket()
+ }
+ }
+}
diff --git a/app/src/main/res/raw/wsd_request.txt b/app/src/main/res/raw/wsd_request.txt
new file mode 100644
index 0000000000..e7ae2d6f85
--- /dev/null
+++ b/app/src/main/res/raw/wsd_request.txt
@@ -0,0 +1,22 @@
+
+
+
+ ##DEST_UUID##
+ http://schemas.xmlsoap.org/ws/2004/09/transfer/Get
+ ##MESSAGE_ID##
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+
+
+ ##MY_UUID##
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/raw/wsdd_discovery.txt b/app/src/main/res/raw/wsdd_discovery.txt
new file mode 100644
index 0000000000..aa39fe0676
--- /dev/null
+++ b/app/src/main/res/raw/wsdd_discovery.txt
@@ -0,0 +1 @@
+urn:schemas-xmlsoap-org:ws:2005:04:discoveryhttp://schemas.xmlsoap.org/ws/2005/04/discovery/Probeurn:uuid:##MY_UUID##wsdp:Device
\ No newline at end of file
diff --git a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/SmbDeleteTaskTest.kt b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/SmbDeleteTaskTest.kt
index 9bd9b32b37..d440d38285 100644
--- a/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/SmbDeleteTaskTest.kt
+++ b/app/src/test/java/com/amaze/filemanager/asynchronous/asynctasks/SmbDeleteTaskTest.kt
@@ -22,7 +22,7 @@ package com.amaze.filemanager.asynchronous.asynctasks
import com.amaze.filemanager.filesystem.HybridFileParcelable
import com.amaze.filemanager.shadows.ShadowSmbUtil.Companion.PATH_CANNOT_DELETE_FILE
-import com.amaze.filemanager.utils.SmbUtil
+import com.amaze.filemanager.utils.smb.SmbUtil
import org.junit.Test
class SmbDeleteTaskTest : AbstractDeleteTaskTestBase() {
diff --git a/app/src/test/java/com/amaze/filemanager/database/UtilitiesDatabaseMigrationTest.kt b/app/src/test/java/com/amaze/filemanager/database/UtilitiesDatabaseMigrationTest.kt
index 85fc4c54a0..e790bfca0f 100644
--- a/app/src/test/java/com/amaze/filemanager/database/UtilitiesDatabaseMigrationTest.kt
+++ b/app/src/test/java/com/amaze/filemanager/database/UtilitiesDatabaseMigrationTest.kt
@@ -39,7 +39,7 @@ import com.amaze.filemanager.database.UtilitiesDatabase.Companion.TABLE_SMB
import com.amaze.filemanager.shadows.ShadowMultiDex
import com.amaze.filemanager.test.ShadowPasswordUtil
import com.amaze.filemanager.utils.PasswordUtil
-import com.amaze.filemanager.utils.SmbUtil
+import com.amaze.filemanager.utils.smb.SmbUtil
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
diff --git a/app/src/test/java/com/amaze/filemanager/database/UtilsHandlerTest.kt b/app/src/test/java/com/amaze/filemanager/database/UtilsHandlerTest.kt
index 9a208729fa..842c7506f8 100644
--- a/app/src/test/java/com/amaze/filemanager/database/UtilsHandlerTest.kt
+++ b/app/src/test/java/com/amaze/filemanager/database/UtilsHandlerTest.kt
@@ -35,7 +35,7 @@ import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.database.models.OperationData
import com.amaze.filemanager.shadows.ShadowMultiDex
import com.amaze.filemanager.test.ShadowPasswordUtil
-import com.amaze.filemanager.utils.SmbUtil
+import com.amaze.filemanager.utils.smb.SmbUtil
import io.reactivex.android.plugins.RxAndroidPlugins
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.schedulers.Schedulers
diff --git a/app/src/test/java/com/amaze/filemanager/utils/ComputerParcelableTest.java b/app/src/test/java/com/amaze/filemanager/utils/ComputerParcelableTest.java
index 1624c290a0..07a1abb70b 100644
--- a/app/src/test/java/com/amaze/filemanager/utils/ComputerParcelableTest.java
+++ b/app/src/test/java/com/amaze/filemanager/utils/ComputerParcelableTest.java
@@ -54,15 +54,15 @@ public void testObjectNotEqualsName() {
/**
* Purpose: when computerParcelable's address and object's address are not the same, confirm that
- * the two are different. Input: computerParcelable.equals(object) only ComputerParcelable.name ==
- * Object.name Expected: result is false
+ * the two are different. Input: computerParcelable.equals(object) only ComputerParcelable.addr ==
+ * Object.name Expected: result is true
*/
@Test
public void testObjectNotEqualsAddr() {
ComputerParcelable computerParcelable = new ComputerParcelable("com1", "1");
Object object = new ComputerParcelable("com1", "2");
- assertFalse(computerParcelable.equals(object));
+ assertTrue(computerParcelable.equals(object));
}
/**
diff --git a/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.kt b/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.kt
index 697fde2f18..352eec1d06 100644
--- a/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.kt
+++ b/app/src/test/java/com/amaze/filemanager/utils/SmbUtilTest.kt
@@ -29,10 +29,10 @@ import com.amaze.filemanager.fileoperations.filesystem.DOESNT_EXIST
import com.amaze.filemanager.fileoperations.filesystem.WRITABLE_ON_REMOTE
import com.amaze.filemanager.shadows.ShadowSmbUtil
import com.amaze.filemanager.test.ShadowPasswordUtil
-import com.amaze.filemanager.utils.SmbUtil.checkFolder
-import com.amaze.filemanager.utils.SmbUtil.createFrom
-import com.amaze.filemanager.utils.SmbUtil.getSmbDecryptedPath
-import com.amaze.filemanager.utils.SmbUtil.getSmbEncryptedPath
+import com.amaze.filemanager.utils.smb.SmbUtil.checkFolder
+import com.amaze.filemanager.utils.smb.SmbUtil.createFrom
+import com.amaze.filemanager.utils.smb.SmbUtil.getSmbDecryptedPath
+import com.amaze.filemanager.utils.smb.SmbUtil.getSmbEncryptedPath
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
diff --git a/app/src/test/java/com/amaze/filemanager/utils/UUIDv5Test.kt b/app/src/test/java/com/amaze/filemanager/utils/UUIDv5Test.kt
new file mode 100644
index 0000000000..6f4261978f
--- /dev/null
+++ b/app/src/test/java/com/amaze/filemanager/utils/UUIDv5Test.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Test for [UUIDv5].
+ */
+class UUIDv5Test {
+
+ /**
+ * Test UUID generation. Value is based on SHA-1 hash, so it can be expected.
+ *
+ * Test case taken (again) from
+ * https://gist.github.com/icedraco/00118b4d3c91d96d8c58e837a448f1b8
+ */
+ @Test
+ fun testGenerateUUID() {
+ val url = "http://www.whatever.com/test/"
+ val uuid = UUIDv5.fromString(UUIDv5.URL, url)
+ assertEquals("1730930d-a36a-5efd-aa3f-561a164f87a4", uuid.toString())
+ }
+}
diff --git a/app/src/test/java/com/amaze/filemanager/utils/smb/AbstractSubnetDiscoverDevicesStrategyTests.kt b/app/src/test/java/com/amaze/filemanager/utils/smb/AbstractSubnetDiscoverDevicesStrategyTests.kt
new file mode 100644
index 0000000000..3b44ccdc1e
--- /dev/null
+++ b/app/src/test/java/com/amaze/filemanager/utils/smb/AbstractSubnetDiscoverDevicesStrategyTests.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils.smb
+
+import android.os.Build.VERSION_CODES
+import android.os.Build.VERSION_CODES.KITKAT
+import android.os.Build.VERSION_CODES.P
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.amaze.filemanager.utils.NetworkUtil
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.mockkStatic
+import io.mockk.unmockkStatic
+import org.junit.After
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import java.net.InetAddress
+
+/**
+ * Base class for [SmbDeviceScannerObservable.DiscoverDeviceStrategy] tests.
+ */
+@RunWith(AndroidJUnit4::class)
+@Config(sdk = [KITKAT, P, VERSION_CODES.R])
+abstract class AbstractSubnetDiscoverDevicesStrategyTests {
+
+ /**
+ * Post test cleanup.
+ */
+ @After
+ open fun tearDown() {
+ unmockkStatic(NetworkUtil::class)
+ }
+
+ protected fun deviceOffline() {
+ mockkStatic(NetworkUtil::class)
+ every { NetworkUtil.isConnectedToWifi(any()) } returns false
+ every { NetworkUtil.isConnectedToLocalNetwork(any()) } returns false
+ every { NetworkUtil.getLocalInetAddress(any()) } returns null
+ }
+
+ protected fun deviceOnline() {
+ mockkStatic(NetworkUtil::class)
+ every { NetworkUtil.isConnectedToWifi(any()) } returns true
+ every { NetworkUtil.isConnectedToLocalNetwork(any()) } returns true
+ every { NetworkUtil.getLocalInetAddress(any()) } returns mockk().also {
+ every { it.hostName } returns "192.168.233.240"
+ }
+ }
+
+ protected fun mockInetAddress(hostName: String, hostAddress: String): InetAddress {
+ val upHost = mockk()
+ every { upHost.hostName } returns hostName
+ every { upHost.hostAddress } returns hostAddress
+ every { InetAddress.getByName(hostAddress) } returns upHost
+ return upHost
+ }
+}
diff --git a/app/src/test/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDevicesStrategyTest.kt b/app/src/test/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDevicesStrategyTest.kt
new file mode 100644
index 0000000000..1cd428f1f6
--- /dev/null
+++ b/app/src/test/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDevicesStrategyTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils.smb
+
+import com.amaze.filemanager.utils.ComputerParcelable
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.slf4j.LoggerFactory
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+class SameSubnetDiscoverDevicesStrategyTest : AbstractSubnetDiscoverDevicesStrategyTests() {
+
+ companion object {
+ @JvmStatic
+ private val logger = LoggerFactory.getLogger(
+ SameSubnetDiscoverDevicesStrategyTest::class.java
+ )
+ }
+
+ /**
+ * Test if device is not connected to network.
+ */
+ @Test
+ fun testDiscoverIfNotConnected() {
+ deviceOffline()
+ val latch = CountDownLatch(1)
+ val result = ArrayList()
+ SameSubnetDiscoverDeviceStrategy().discoverDevices {
+ result.add(it)
+ latch.countDown()
+ }
+ try {
+ latch.await(1, TimeUnit.SECONDS)
+ } catch (_: Throwable) {
+ latch.countDown()
+ }
+ assertEquals(0, result.size)
+ }
+}
diff --git a/app/src/test/java/com/amaze/filemanager/utils/smb/WsddSubnetDiscoverDevicesStrategyTest.kt b/app/src/test/java/com/amaze/filemanager/utils/smb/WsddSubnetDiscoverDevicesStrategyTest.kt
new file mode 100644
index 0000000000..0212756d3b
--- /dev/null
+++ b/app/src/test/java/com/amaze/filemanager/utils/smb/WsddSubnetDiscoverDevicesStrategyTest.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.utils.smb
+
+import com.amaze.filemanager.test.randomBytes
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.robolectric.util.ReflectionHelpers
+import java.util.*
+import kotlin.text.Charsets.UTF_8
+
+/**
+ * Test [WsddDiscoverDeviceStrategy].
+ */
+@Suppress("StringLiteralDuplication")
+class WsddSubnetDiscoverDevicesStrategyTest : AbstractSubnetDiscoverDevicesStrategyTests() {
+
+ private val multicastResponseTemplate = javaClass.classLoader!!
+ .getResourceAsStream("wsdd/multicast-response.txt")
+ .reader(UTF_8).readText()
+
+ private val wsdResponseTemplate = javaClass.classLoader!!
+ .getResourceAsStream("wsdd/wsd-response.txt")
+ .reader(UTF_8).readText()
+
+ private val parseXmlForResponse:
+ (WsddDiscoverDeviceStrategy, Any, Array) -> Map =
+ { instance, xml, tags ->
+ require((xml is ByteArray) or (xml is String))
+ ReflectionHelpers.callInstanceMethod(
+ WsddDiscoverDeviceStrategy::class.java,
+ instance,
+ "parseXmlForResponse",
+ ReflectionHelpers.ClassParameter(xml.javaClass, xml),
+ ReflectionHelpers.ClassParameter(Array::class.java, tags)
+ )
+ }
+
+ private lateinit var wsdMulticastResponseMessageId: String
+ private lateinit var deviceId: String
+
+ /**
+ * Test for normal parsing of multicast response
+ */
+ @Test
+ fun testParseMulticastResponse() {
+ val instance = WsddDiscoverDeviceStrategy()
+ val result = parseXmlForResponse.invoke(
+ instance,
+ createMulticastResponse(),
+ arrayOf("wsd:Types", "wsa:Address")
+ )
+ assertTrue(result.isNotEmpty())
+ assertTrue(result.containsKey("wsd:Types"))
+ assertTrue(result.containsKey("wsa:Address"))
+ assertTrue(true == result["wsd:Types"]?.isNotBlank())
+ assertTrue(true == result["wsa:Address"]?.isNotBlank())
+ }
+
+ /**
+ * Test parsing invalid XML and/or invalid/nonexistent tags in XML.
+ */
+ @Test
+ fun testParseInvalidMulticastResponse() {
+ val instance = WsddDiscoverDeviceStrategy()
+ assertTrue(parseXmlForResponse.invoke(instance, "", emptyArray()).isEmpty())
+ assertTrue(parseXmlForResponse.invoke(instance, "foobar", emptyArray()).isEmpty())
+ assertTrue(parseXmlForResponse.invoke(instance, "", emptyArray()).isEmpty())
+ assertTrue(
+ parseXmlForResponse.invoke(
+ instance,
+ ByteArray(0),
+ emptyArray()
+ ).isEmpty()
+ )
+ assertTrue(
+ parseXmlForResponse.invoke(
+ instance,
+ "foobar".toByteArray(),
+ emptyArray()
+ ).isEmpty()
+ )
+ assertTrue(
+ parseXmlForResponse.invoke(
+ instance,
+ randomBytes(),
+ emptyArray()
+ ).isEmpty()
+ )
+ }
+
+ /**
+ * Test parsing of valid XML but with non-matching tags in XML.
+ */
+ @Test
+ fun testParseNonMatchingMulticastResponseParams() {
+ val instance = WsddDiscoverDeviceStrategy()
+ assertEquals(
+ 0,
+ parseXmlForResponse.invoke(
+ instance,
+ "",
+ arrayOf("foobar")
+ ).size
+ )
+ assertEquals(
+ 0,
+ parseXmlForResponse.invoke(
+ instance,
+ "",
+ arrayOf("test")
+ ).size
+ )
+ }
+
+ private fun createMulticastResponse(): String {
+ return multicastResponseTemplate.replace(
+ "##DEVICE_UUID##",
+ UUID.randomUUID().toString()
+ ).replace(
+ "##MESSAGE_ID##",
+ UUID.randomUUID().toString()
+ ).replace(
+ "##SRC_MESSAGE_ID##",
+ UUID.randomUUID().toString()
+ )
+ }
+
+ private fun generateWsdResponse(deviceName: String, workgroupName: String = "WORKGROUP") =
+ wsdResponseTemplate
+ .replace("##THIS_DEVICE_ID##", deviceId)
+ .replace("##DEVICE_NAME##", deviceName)
+ .replace("##WORKGROUP_NAME##", workgroupName)
+ .replace("##PREV_MESSAGE_ID##", wsdMulticastResponseMessageId)
+ .replace("##THIS_MESSAGE_ID##", UUID.randomUUID().toString())
+ .toByteArray(UTF_8)
+}
diff --git a/app/src/test/resources/wsdd/multicast-response.txt b/app/src/test/resources/wsdd/multicast-response.txt
new file mode 100644
index 0000000000..5a8af6aa9c
--- /dev/null
+++ b/app/src/test/resources/wsdd/multicast-response.txt
@@ -0,0 +1,28 @@
+
+
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+ http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches
+ urn:uuid:##MESSAGE_ID##
+ urn:uuid:##SRC_MESSAGE_ID##
+
+
+
+
+
+
+ urn:uuid:##DEVICE_UUID##
+
+ wsdp:Device pub:Computer
+ 1
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/resources/wsdd/wsd-response.txt b/app/src/test/resources/wsdd/wsd-response.txt
new file mode 100644
index 0000000000..7eb119a43f
--- /dev/null
+++ b/app/src/test/resources/wsdd/wsd-response.txt
@@ -0,0 +1,46 @@
+
+
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+ http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse
+ urn:uuid:##THIS_MESSAGE_ID##
+ urn:uuid:##PREV_MESSAGE_ID##
+
+
+
+
+
+ WSD Device ##DEVICE_NAME##
+ 1.0
+ 1
+
+
+
+
+ wsdd
+ wsdd
+ Computers
+
+
+
+
+
+
+ urn:uuid:##THIS_DEVICE_ID##
+
+ pub:Computer
+ urn:uuid:##THIS_DEVICE_ID##
+ ##DEVICE_NAME##/Workgroup:##WORKGROUP_NAME##
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7197d6553f..3fd0ef3566 100644
--- a/build.gradle
+++ b/build.gradle
@@ -37,6 +37,8 @@ buildscript {
commonsNetVersion = "3.8.0"
ftpserverVersion = "1.1.1"
jsoupVersion = "1.13.1"
+ rxAndroidVersion = "2.1.1"
+ rxJavaVersion = "2.2.9"
}
repositories {
google()
@@ -76,7 +78,7 @@ allprojects {
spotless {
java {
licenseHeaderFile 'spotless.license-java'
- target 'app/src/**/*.java', 'commons_compress_7z/src/**/*.java', 'file_operations/src/**/*.java'
+ target 'app/src/**/*.java', 'commons_compress_7z/src/**/*.java', 'file_operations/src/**/*.java', 'portscanner/src/**/*.java'
googleJavaFormat('1.15.0')
removeUnusedImports() // removes any unused imports
importOrder 'java', 'javax', 'org', 'com', 'android', 'androidx', ''
diff --git a/portscanner/.gitignore b/portscanner/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/portscanner/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/portscanner/build.gradle b/portscanner/build.gradle
new file mode 100644
index 0000000000..355d17f1c3
--- /dev/null
+++ b/portscanner/build.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-parcelize'
+
+android {
+ namespace 'com.stealthcopter.networktools'
+ compileSdk 32
+
+ defaultConfig {
+ minSdk 14
+ targetSdk 32
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
+ // Because RxAndroid releases are few and far between, it is recommended you also
+ // explicitly depend on RxJava's latest version for bug fixes and new features.
+ // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
+ implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
+ testImplementation 'junit:junit:4.13.2'
+}
\ No newline at end of file
diff --git a/portscanner/consumer-rules.pro b/portscanner/consumer-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/portscanner/proguard-rules.pro b/portscanner/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/portscanner/proguard-rules.pro
@@ -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
\ No newline at end of file
diff --git a/portscanner/src/main/AndroidManifest.xml b/portscanner/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..100ab263c3
--- /dev/null
+++ b/portscanner/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/portscanner/src/main/java/com/stealthcopter/networktools/IPTools.kt b/portscanner/src/main/java/com/stealthcopter/networktools/IPTools.kt
new file mode 100644
index 0000000000..59fac0768e
--- /dev/null
+++ b/portscanner/src/main/java/com/stealthcopter/networktools/IPTools.kt
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.stealthcopter.networktools
+
+import java.net.Inet4Address
+import java.net.InetAddress
+import java.net.NetworkInterface
+import java.net.SocketException
+import java.util.regex.Pattern
+
+/**
+ * Created by mat on 14/12/15.
+ */
+object IPTools {
+ /*
+ * Ip matching patterns from
+ * https://examples.javacodegeeks.com/core-java/util/regex/regular-expressions-for-ip-v4-and-ip-v6-addresses/
+ * note that these patterns will match most but not all valid ips
+ */
+ private val IPV4_PATTERN = Pattern.compile(
+ "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"
+ )
+ private val IPV6_STD_PATTERN = Pattern.compile(
+ "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"
+ )
+ private val IPV6_HEX_COMPRESSED_PATTERN = Pattern.compile(
+ "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"
+ )
+
+ /**
+ * Answers if given string is valid IPv4 address.
+ */
+ @JvmStatic
+ fun isIPv4Address(address: String?): Boolean {
+ return address != null && IPV4_PATTERN.matcher(address).matches()
+ }
+
+ /**
+ * Answers if given string is valid IPv6 address in long form.
+ */
+ @JvmStatic
+ fun isIPv6StdAddress(address: String?): Boolean {
+ return address != null && IPV6_STD_PATTERN.matcher(address).matches()
+ }
+
+ /**
+ * Answers if given string is valid IPv6 address in hex compressed form.
+ */
+ @JvmStatic
+ fun isIPv6HexCompressedAddress(address: String?): Boolean {
+ return address != null && IPV6_HEX_COMPRESSED_PATTERN.matcher(address).matches()
+ }
+
+ /**
+ * Answers if given string is a valid IPv6 address.
+ */
+ @JvmStatic
+ fun isIPv6Address(address: String?): Boolean {
+ return address != null && (isIPv6StdAddress(address) || isIPv6HexCompressedAddress(address))
+ }
+
+ /*
+ * @return The first local IPv4 address, or null
+ */
+ @JvmStatic
+ val localIPv4Address: InetAddress?
+ get() {
+ val localAddresses = localIPv4Addresses
+ return if (localAddresses.isNotEmpty()) localAddresses[0] else null
+ }
+
+ /*
+ * Return The list of all IPv4 addresses found
+ */
+ private val localIPv4Addresses: List
+ get() = runCatching {
+ NetworkInterface.getNetworkInterfaces().toList().flatMap { iface ->
+ iface.inetAddresses.asSequence().filter { addr ->
+ addr is Inet4Address && !addr.isLoopbackAddress()
+ }
+ }
+ }.getOrDefault(emptyList())
+
+ /**
+ * Check if the provided ip address refers to the localhost
+ *
+ * https://stackoverflow.com/a/2406819/315998
+ *
+ * @param addr - address to check
+ * @return - true if ip address is self
+ */
+ @JvmStatic
+ fun isIpAddressLocalhost(addr: InetAddress?): Boolean {
+ return addr?.run {
+ // Check if the address is a valid special local or loop back
+ if (addr.isAnyLocalAddress || addr.isLoopbackAddress) true else try {
+ NetworkInterface.getByInetAddress(addr) != null
+ } catch (e: SocketException) {
+ false
+ }
+ } ?: false
+ }
+
+ /**
+ * Check if the provided ip address refers to the localhost
+ *
+ * https://stackoverflow.com/a/2406819/315998
+ *
+ * @param addr - address to check
+ * @return - true if ip address is self
+ */
+ @JvmStatic
+ fun isIpAddressLocalNetwork(addr: InetAddress?): Boolean =
+ addr != null && addr.isSiteLocalAddress
+}
diff --git a/portscanner/src/main/java/com/stealthcopter/networktools/PortScan.kt b/portscanner/src/main/java/com/stealthcopter/networktools/PortScan.kt
new file mode 100644
index 0000000000..0f9f9812ef
--- /dev/null
+++ b/portscanner/src/main/java/com/stealthcopter/networktools/PortScan.kt
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.stealthcopter.networktools
+
+import com.stealthcopter.networktools.IPTools.isIpAddressLocalNetwork
+import com.stealthcopter.networktools.IPTools.isIpAddressLocalhost
+import com.stealthcopter.networktools.portscanning.PortScanTCP
+import com.stealthcopter.networktools.portscanning.PortScanUDP
+import io.reactivex.Flowable
+import io.reactivex.schedulers.Schedulers
+import java.net.InetAddress
+import java.net.UnknownHostException
+import java.util.*
+
+/** Created by mat on 14/12/15. */
+class PortScan // This class is not to be instantiated
+private constructor() {
+
+ private var method = METHOD_TCP
+ private var noThreads = 50
+ private var address: InetAddress? = null
+ private var timeOutMillis = 1000
+ private var cancelled = false
+ private var ports: MutableList = ArrayList()
+ private val openPortsFound: MutableList = ArrayList()
+ private var portListener: PortListener? = null
+ private lateinit var runningFlowable: Flowable
+
+ interface PortListener {
+ /**
+ * Callback function for port scan result
+ */
+ fun onResult(portNo: Int, open: Boolean)
+
+ /**
+ * Callback function for receiving the list of opened ports
+ */
+ fun onFinished(openPorts: List?)
+ }
+
+ /**
+ * Sets the timeout for each port scanned
+ *
+ * If you raise the timeout you may want to consider increasing the thread count [ ][.setNoThreads] to compensate. We can afford to have quite a high thread count as most of
+ * the time the thread is just sitting idle and waiting for the socket to timeout.
+ *
+ * @param timeOutMillis - the timeout for each ping in milliseconds Recommendations: Local host:
+ * 20 - 500 ms - can be very fast as request doesn't need to go over network Local network 500
+ * - 2500 ms Remote Scan 2500+ ms
+ * @return this object to allow chaining
+ */
+ fun setTimeOutMillis(timeOutMillis: Int): PortScan {
+ require(timeOutMillis >= 0) { "Timeout cannot be less than 0" }
+ this.timeOutMillis = timeOutMillis
+ return this
+ }
+
+ /**
+ * Scan the ports to scan
+ *
+ * @param port - the port to scan
+ * @return this object to allow chaining
+ */
+ fun setPort(port: Int): PortScan {
+ ports.clear()
+ validatePort(port)
+ ports.add(port)
+ return this
+ }
+
+ /**
+ * Scan the ports to scan
+ *
+ * @param ports - the ports to scan
+ * @return this object to allow chaining
+ */
+ fun setPorts(ports: MutableList): PortScan {
+ // Check all ports are valid
+ for (port in ports) {
+ validatePort(port)
+ }
+ this.ports = ports
+ return this
+ }
+
+ /**
+ * Scan the ports to scan
+ *
+ * @param portString - the ports to scan (comma separated, hyphen denotes a range). For example:
+ * "21-23,25,45,53,80"
+ * @return this object to allow chaining
+ */
+ fun setPorts(portString: String): PortScan {
+ var portString = portString
+ ports.clear()
+ val ports: MutableList = ArrayList()
+ portString = portString.substring(portString.indexOf(":") + 1, portString.length)
+ for (x in portString.split(",").toTypedArray()) {
+ if (x.contains("-")) {
+ val start = x.split("-").toTypedArray()[0].toInt()
+ val end = x.split("-").toTypedArray()[1].toInt()
+ validatePort(start)
+ validatePort(end)
+ require(end > start) { "Start port cannot be greater than or equal to the end port" }
+ for (j in start..end) {
+ ports.add(j)
+ }
+ } else {
+ val start = x.toInt()
+ validatePort(start)
+ ports.add(start)
+ }
+ }
+ this.ports = ports
+ return this
+ }
+
+ /**
+ * Checks and throws exception if port is not valid
+ *
+ * @param port - the port to validate
+ */
+ private fun validatePort(port: Int) {
+ require(port >= 1) { "Start port cannot be less than 1" }
+ require(port <= 65535) { "Start cannot be greater than 65535" }
+ }
+
+ private fun setAddress(address: InetAddress) {
+ this.address = address
+ }
+
+ private fun setDefaultThreadsAndTimeouts() {
+ // Try and work out automatically what kind of host we are scanning
+ // local host (this device) / local network / remote
+ if (isIpAddressLocalhost(address)) {
+ // If we are scanning a the localhost set the timeout to be very short so we get faster
+ // results
+ // This will be overridden if user calls setTimeoutMillis manually.
+ timeOutMillis = TIMEOUT_LOCALHOST
+ noThreads = DEFAULT_THREADS_LOCALHOST
+ } else if (isIpAddressLocalNetwork(address)) {
+ // Assume local network (not infallible)
+ timeOutMillis = TIMEOUT_LOCALNETWORK
+ noThreads = DEFAULT_THREADS_LOCALNETWORK
+ } else {
+ // Assume remote network timeouts
+ timeOutMillis = TIMEOUT_REMOTE
+ noThreads = DEFAULT_THREADS_REMOTE
+ }
+ }
+
+ /**
+ * @param noThreads set the number of threads to work with, note we default to a large number as
+ * these requests are network heavy not cpu heavy.
+ * @return self
+ * @throws IllegalArgumentException - if no threads is less than 1
+ */
+ @Throws(IllegalArgumentException::class)
+ fun setNoThreads(noThreads: Int): PortScan {
+ require(noThreads >= 1) { "Cannot have less than 1 thread" }
+ this.noThreads = noThreads
+ return this
+ }
+
+ /**
+ * Set scan method, either TCP or UDP
+ *
+ * @param method - the transport method to use to scan, either PortScan.METHOD_UDP or
+ * PortScan.METHOD_TCP
+ * @return this object to allow chaining
+ * @throws IllegalArgumentException - if invalid method
+ */
+ private fun setMethod(method: Int): PortScan {
+ when (method) {
+ METHOD_UDP, METHOD_TCP -> this.method = method
+ else -> throw IllegalArgumentException("Invalid method type $method")
+ }
+ return this
+ }
+
+ /**
+ * Set scan method to UDP
+ *
+ * @return this object to allow chaining
+ */
+ fun setMethodUDP(): PortScan {
+ setMethod(METHOD_UDP)
+ return this
+ }
+
+ /**
+ * Set scan method to TCP
+ *
+ * @return this object to allow chaining
+ */
+ fun setMethodTCP(): PortScan {
+ setMethod(METHOD_TCP)
+ return this
+ }
+
+ /** Cancel a running ping */
+ fun cancel() {
+ cancelled = true
+ runningFlowable.unsubscribeOn(Schedulers.computation())
+ }
+
+ /**
+ * Perform a synchronous (blocking) port scan and return a list of open ports
+ *
+ * @return - ping result
+ */
+ fun doScan(): List {
+ cancelled = false
+ openPortsFound.clear()
+ runningFlowable = createPortScanFlowable().doOnComplete {
+ openPortsFound.sort()
+ }
+ runningFlowable.blockingSubscribe()
+ return openPortsFound
+ }
+
+ private fun createPortScanFlowable(): Flowable {
+ return Flowable.fromIterable(ports)
+ .parallel(noThreads)
+ .runOn(Schedulers.io())
+ .map { portNo ->
+ PortScanRunnable(address, portNo, timeOutMillis, method).run()
+ }.sequential()
+ .subscribeOn(Schedulers.computation())
+ }
+
+ @Synchronized
+ private fun portScanned(port: Int, open: Boolean) {
+ if (open) {
+ openPortsFound.add(port)
+ }
+ portListener?.onResult(port, open)
+ }
+
+ private inner class PortScanRunnable constructor(
+ private val address: InetAddress?,
+ private val portNo: Int,
+ private val timeOutMillis: Int,
+ private val method: Int
+ ) : Runnable {
+ override fun run() {
+ if (cancelled) return
+ when (method) {
+ METHOD_UDP -> portScanned(
+ portNo,
+ PortScanUDP.scanAddress(address, portNo, timeOutMillis)
+ )
+ METHOD_TCP -> portScanned(
+ portNo,
+ PortScanTCP.scanAddress(address, portNo, timeOutMillis)
+ )
+ else -> throw IllegalArgumentException("Invalid method")
+ }
+ }
+ }
+
+ companion object {
+ private const val TIMEOUT_LOCALHOST = 25
+ private const val TIMEOUT_LOCALNETWORK = 1000
+ private const val TIMEOUT_REMOTE = 2500
+ private const val DEFAULT_THREADS_LOCALHOST = 7
+ private const val DEFAULT_THREADS_LOCALNETWORK = 50
+ private const val DEFAULT_THREADS_REMOTE = 50
+ private const val METHOD_TCP = 0
+ private const val METHOD_UDP = 1
+
+ /**
+ * Set the address to ping
+ *
+ * @param address - Address to be pinged
+ * @return this object to allow chaining
+ * @throws UnknownHostException - if no IP address for the `host` could be found, or if a
+ * scope_id was specified for a global IPv6 address.
+ */
+ @JvmStatic
+ @Throws(UnknownHostException::class)
+ fun onAddress(address: String?): PortScan {
+ return onAddress(InetAddress.getByName(address))
+ }
+
+ /**
+ * Set the address to ping
+ *
+ * @param ia - Address to be pinged
+ * @return this object to allow chaining
+ */
+ @JvmStatic
+ fun onAddress(ia: InetAddress): PortScan {
+ val portScan = PortScan()
+ portScan.setAddress(ia)
+ portScan.setDefaultThreadsAndTimeouts()
+ return portScan
+ }
+ }
+}
diff --git a/portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanTCP.kt b/portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanTCP.kt
new file mode 100644
index 0000000000..8c816b155d
--- /dev/null
+++ b/portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanTCP.kt
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.stealthcopter.networktools.portscanning
+
+import java.net.InetAddress
+import java.net.InetSocketAddress
+import java.net.Socket
+
+/**
+ * Created by mat on 13/12/15.
+ */
+object PortScanTCP {
+ /**
+ * Check if a port is open with TCP
+ *
+ * @param ia - address to scan
+ * @param portNo - port to scan
+ * @param timeoutMillis - timeout
+ * @return - true if port is open, false if not or unknown
+ */
+ @JvmStatic
+ @Suppress("LabeledExpression")
+ fun scanAddress(ia: InetAddress?, portNo: Int, timeoutMillis: Int): Boolean {
+ return Socket().let { s ->
+ runCatching {
+ s.connect(InetSocketAddress(ia, portNo), timeoutMillis)
+ return@let true
+ }.also {
+ runCatching {
+ s.close()
+ }.getOrNull()
+ }.getOrDefault(false)
+ }
+ }
+}
diff --git a/portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanUDP.kt b/portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanUDP.kt
new file mode 100644
index 0000000000..286d6e7e01
--- /dev/null
+++ b/portscanner/src/main/java/com/stealthcopter/networktools/portscanning/PortScanUDP.kt
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.stealthcopter.networktools.portscanning
+
+import java.lang.Exception
+import java.net.DatagramPacket
+import java.net.DatagramSocket
+import java.net.InetAddress
+import java.net.SocketTimeoutException
+
+/**
+ * Created by mat on 13/12/15.
+ */
+object PortScanUDP {
+ /**
+ * Check if a port is open with UDP, note that this isn't reliable
+ * as UDP will does not send ACKs
+ *
+ * @param ia - address to scan
+ * @param portNo - port to scan
+ * @param timeoutMillis - timeout
+ * @return - true if port is open, false if not or unknown
+ */
+ @JvmStatic
+ fun scanAddress(ia: InetAddress?, portNo: Int, timeoutMillis: Int): Boolean {
+ try {
+ val bytes = ByteArray(128)
+ val dp = DatagramPacket(bytes, bytes.size)
+ val ds = DatagramSocket()
+ ds.soTimeout = timeoutMillis
+ ds.connect(ia, portNo)
+ ds.send(dp)
+ ds.isConnected
+ ds.receive(dp)
+ ds.close()
+ } catch (e: SocketTimeoutException) {
+ return true
+ } catch (ignore: Exception) {
+ }
+ return false
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 4f9571f586..70edc43f9d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,3 @@
include ':file_operations'
+include ':portscanner'
include ':app', ':commons_compress_7z'
diff --git a/testShared/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt b/testShared/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt
index cdc2551c50..b141acd655 100644
--- a/testShared/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt
+++ b/testShared/src/test/java/com/amaze/filemanager/shadows/ShadowSmbUtil.kt
@@ -21,7 +21,7 @@
package com.amaze.filemanager.shadows
import android.content.Context
-import com.amaze.filemanager.utils.SmbUtil
+import com.amaze.filemanager.utils.smb.SmbUtil
import jcifs.context.SingletonContext
import jcifs.smb.SmbException
import jcifs.smb.SmbFile
diff --git a/testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt b/testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt
new file mode 100644
index 0000000000..f859a35b43
--- /dev/null
+++ b/testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt
@@ -0,0 +1,51 @@
+package com.amaze.filemanager.test.volley
+
+import com.android.volley.AuthFailureError
+import com.android.volley.Request
+import com.android.volley.toolbox.BaseHttpStack
+import com.android.volley.toolbox.HttpResponse
+import java.io.IOException
+
+/**
+ * Mock [BaseHttpStack] for test only.
+ */
+class MockHttpStack : BaseHttpStack() {
+
+ private lateinit var mResponseToReturn: HttpResponse
+ private lateinit var lastUrl: String
+ private lateinit var mLastHeaders: MutableMap
+ private var lastPostBody: ByteArray? = null
+
+ /**
+ * get headers in last request
+ */
+ fun getLastHeaders() = mLastHeaders
+
+ /**
+ * Manually set response to return
+ */
+ fun setResponseToReturn(response: HttpResponse) {
+ mResponseToReturn = response
+ }
+
+ @Throws(IOException::class, AuthFailureError::class)
+ override fun executeRequest(
+ request: Request<*>,
+ additionalHeaders: Map?
+ ): HttpResponse {
+ lastUrl = request.url
+ mLastHeaders = HashMap()
+ if (request.headers != null) {
+ mLastHeaders.putAll(request.headers)
+ }
+ if (additionalHeaders != null) {
+ mLastHeaders.putAll(additionalHeaders)
+ }
+ try {
+ lastPostBody = request.body
+ } catch (e: AuthFailureError) {
+ lastPostBody = null
+ }
+ return mResponseToReturn
+ }
+}
From 72d2774c1cd368e4649603b12afe94c071ead73e Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 6 Jun 2023 22:54:35 +0530
Subject: [PATCH 023/328] move basic search off of main thread
Signed-off-by: VishnuSanal
---
.../ui/activities/MainActivityViewModel.kt | 38 +++++++++++++++++
.../ui/views/appbar/SearchView.java | 41 ++++++-------------
2 files changed, 51 insertions(+), 28 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
index dc01bebcfd..bc8579a436 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
@@ -23,10 +23,17 @@ package com.amaze.filemanager.ui.activities
import android.app.Application
import androidx.collection.LruCache
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
+import androidx.preference.PreferenceManager
import com.amaze.filemanager.adapters.data.LayoutElementParcelable
+import com.amaze.filemanager.fileoperations.filesystem.OpenMode
+import com.amaze.filemanager.filesystem.HybridFileParcelable
+import com.amaze.filemanager.filesystem.root.ListFilesCommand.listFiles
+import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import java.util.Locale
class MainActivityViewModel(val applicationContext: Application) :
AndroidViewModel(applicationContext) {
@@ -72,4 +79,35 @@ class MainActivityViewModel(val applicationContext: Application) :
fun getFromMediaFilesCache(mediaType: Int): List? {
return mediaCacheHash[mediaType]
}
+
+ fun basicSearch(s: String, mainActivity: MainActivity) : MutableLiveData> {
+
+ val hybridFileParcelables = ArrayList()
+
+ val mutableLiveData: MutableLiveData> = MutableLiveData(hybridFileParcelables)
+
+ val showHiddenFiles = PreferenceManager
+ .getDefaultSharedPreferences(mainActivity)
+ .getBoolean(PREFERENCE_SHOW_HIDDENFILES, false)
+
+ viewModelScope.launch(Dispatchers.IO) {
+ listFiles(
+ mainActivity.currentMainFragment!!.currentPath!!,
+ mainActivity.isRootExplorer,
+ showHiddenFiles,
+ { _: OpenMode? -> null }
+ ) { hybridFileParcelable: HybridFileParcelable ->
+ if (hybridFileParcelable.getName(mainActivity)
+ .lowercase(Locale.getDefault())
+ .contains(s.lowercase(Locale.getDefault()))
+ && (showHiddenFiles || !hybridFileParcelable.isHidden)) {
+ hybridFileParcelables.add(hybridFileParcelable)
+
+ mutableLiveData.postValue(hybridFileParcelables)
+ }
+ }
+ }
+
+ return mutableLiveData
+ }
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index f9a2b3e1a3..0aabf3c3a4 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -32,7 +32,6 @@
import com.amaze.filemanager.adapters.SearchRecyclerViewAdapter;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
import com.amaze.filemanager.filesystem.RootHelper;
-import com.amaze.filemanager.filesystem.root.ListFilesCommand;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
import com.amaze.filemanager.ui.theme.AppTheme;
@@ -208,14 +207,14 @@ private boolean onSearch(boolean shouldSave) {
return false;
}
- search(s);
+ basicSearch(s);
if (shouldSave) saveRecentPreference(s);
return true;
}
- private void search(String s) {
+ private void basicSearch(String s) {
clearRecyclerView();
@@ -228,30 +227,16 @@ private void search(String s) {
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_indexed_search)));
- ArrayList hybridFileParcelables = new ArrayList<>();
-
- boolean showHiddenFiles =
- PreferenceManager.getDefaultSharedPreferences(mainActivity)
- .getBoolean(PREFERENCE_SHOW_HIDDENFILES, false);
-
- // TODO: takes too much resources & freezes main thread on huge folders
- ListFilesCommand.INSTANCE.listFiles(
- mainActivity.getCurrentMainFragment().getPath(),
- mainActivity.isRootExplorer(),
- showHiddenFiles,
- mode -> null,
- hybridFileParcelable -> {
- if (hybridFileParcelable.getName(mainActivity).toLowerCase().contains(s.toLowerCase())
- && (showHiddenFiles || !hybridFileParcelable.isHidden())) {
-
- hybridFileParcelables.add(hybridFileParcelable);
-
- searchRecyclerViewAdapter.submitList(hybridFileParcelables);
-
- searchRecyclerViewAdapter.notifyItemInserted(hybridFileParcelables.size() + 1);
- }
- return null;
- });
+ mainActivity
+ .getCurrentMainFragment()
+ .getMainActivityViewModel()
+ .basicSearch(s, mainActivity)
+ .observe(
+ mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
+ hybridFileParcelables -> {
+ searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+ searchRecyclerViewAdapter.notifyItemInserted(hybridFileParcelables.size() + 1);
+ });
}
private void saveRecentPreference(String s) {
@@ -315,7 +300,7 @@ private void initRecentSearches(Context context) {
Utils.hideKeyboard(mainActivity);
- search(s);
+ basicSearch(s);
});
}
}
From db1380718910e59915b7ce7c27ad728749f2a4ee Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 6 Jun 2023 22:56:10 +0530
Subject: [PATCH 024/328] chore: spotless
Signed-off-by: VishnuSanal
---
.../ui/activities/MainActivityViewModel.kt | 8 ++++----
.../filemanager/ui/views/appbar/SearchView.java | 12 ++++++------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
index bc8579a436..aa7c537759 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
@@ -80,8 +80,7 @@ class MainActivityViewModel(val applicationContext: Application) :
return mediaCacheHash[mediaType]
}
- fun basicSearch(s: String, mainActivity: MainActivity) : MutableLiveData> {
-
+ fun basicSearch(s: String, mainActivity: MainActivity): MutableLiveData> {
val hybridFileParcelables = ArrayList()
val mutableLiveData: MutableLiveData> = MutableLiveData(hybridFileParcelables)
@@ -99,8 +98,9 @@ class MainActivityViewModel(val applicationContext: Application) :
) { hybridFileParcelable: HybridFileParcelable ->
if (hybridFileParcelable.getName(mainActivity)
.lowercase(Locale.getDefault())
- .contains(s.lowercase(Locale.getDefault()))
- && (showHiddenFiles || !hybridFileParcelable.isHidden)) {
+ .contains(s.lowercase(Locale.getDefault())) &&
+ (showHiddenFiles || !hybridFileParcelable.isHidden)
+ ) {
hybridFileParcelables.add(hybridFileParcelable)
mutableLiveData.postValue(hybridFileParcelables)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 0aabf3c3a4..53b44d7d15 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -228,12 +228,12 @@ private void basicSearch(String s) {
mainActivity.getString(R.string.try_indexed_search)));
mainActivity
- .getCurrentMainFragment()
- .getMainActivityViewModel()
- .basicSearch(s, mainActivity)
- .observe(
- mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
- hybridFileParcelables -> {
+ .getCurrentMainFragment()
+ .getMainActivityViewModel()
+ .basicSearch(s, mainActivity)
+ .observe(
+ mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
+ hybridFileParcelables -> {
searchRecyclerViewAdapter.submitList(hybridFileParcelables);
searchRecyclerViewAdapter.notifyItemInserted(hybridFileParcelables.size() + 1);
});
From 7573836fadc375ddf11ae037d3175e3a1b3892af Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 6 Jun 2023 23:18:59 +0530
Subject: [PATCH 025/328] move indexed search off of main thread
Signed-off-by: VishnuSanal
---
.../ui/activities/MainActivityViewModel.kt | 58 ++++++++++++++++++-
.../ui/views/appbar/SearchView.java | 51 ++++------------
2 files changed, 67 insertions(+), 42 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
index aa7c537759..103aa2a643 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
@@ -21,6 +21,7 @@
package com.amaze.filemanager.ui.activities
import android.app.Application
+import android.provider.MediaStore
import androidx.collection.LruCache
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
@@ -29,10 +30,12 @@ import androidx.preference.PreferenceManager
import com.amaze.filemanager.adapters.data.LayoutElementParcelable
import com.amaze.filemanager.fileoperations.filesystem.OpenMode
import com.amaze.filemanager.filesystem.HybridFileParcelable
+import com.amaze.filemanager.filesystem.RootHelper
import com.amaze.filemanager.filesystem.root.ListFilesCommand.listFiles
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import java.io.File
import java.util.Locale
class MainActivityViewModel(val applicationContext: Application) :
@@ -80,7 +83,7 @@ class MainActivityViewModel(val applicationContext: Application) :
return mediaCacheHash[mediaType]
}
- fun basicSearch(s: String, mainActivity: MainActivity): MutableLiveData> {
+ fun basicSearch(mainActivity: MainActivity, query: String): MutableLiveData> {
val hybridFileParcelables = ArrayList()
val mutableLiveData: MutableLiveData> = MutableLiveData(hybridFileParcelables)
@@ -98,7 +101,7 @@ class MainActivityViewModel(val applicationContext: Application) :
) { hybridFileParcelable: HybridFileParcelable ->
if (hybridFileParcelable.getName(mainActivity)
.lowercase(Locale.getDefault())
- .contains(s.lowercase(Locale.getDefault())) &&
+ .contains(query.lowercase(Locale.getDefault())) &&
(showHiddenFiles || !hybridFileParcelable.isHidden)
) {
hybridFileParcelables.add(hybridFileParcelable)
@@ -110,4 +113,55 @@ class MainActivityViewModel(val applicationContext: Application) :
return mutableLiveData
}
+
+ fun indexedSearch(
+ mainActivity: MainActivity,
+ query: String,
+ ): MutableLiveData< ArrayList > {
+
+ val list = ArrayList()
+
+ val mutableLiveData: MutableLiveData> = MutableLiveData(list)
+
+ val showHiddenFiles =
+ PreferenceManager.getDefaultSharedPreferences(mainActivity)
+ .getBoolean(PREFERENCE_SHOW_HIDDENFILES, false)
+
+ viewModelScope.launch(Dispatchers.IO) {
+
+ val projection = arrayOf(MediaStore.Files.FileColumns.DATA)
+
+ val cursor = mainActivity
+ .contentResolver
+ .query(MediaStore.Files.getContentUri("external"), projection, null, null, null)
+ ?: return@launch
+
+ if (cursor.count > 0 && cursor.moveToFirst()) {
+ do {
+ val path =
+ cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
+
+ if (path != null
+ && path.contains(mainActivity.currentMainFragment?.currentPath!!)
+ && File(path).name.lowercase(Locale.getDefault()).contains(
+ query.lowercase(Locale.getDefault())
+ )
+ ) {
+
+ val hybridFileParcelable =
+ RootHelper.generateBaseFile(File(path), showHiddenFiles)
+
+ if (hybridFileParcelable != null) {
+ list.add(hybridFileParcelable)
+ mutableLiveData.postValue(list)
+ }
+ }
+ } while (cursor.moveToNext())
+ }
+
+ cursor.close()
+ }
+
+ return mutableLiveData
+ }
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 53b44d7d15..4a513b2825 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -172,10 +172,16 @@ public void afterTextChanged(Editable s) {}
if (searchMode == 1) {
- List hybridFileParcelables = indexedSearch(mainActivity, s);
-
- searchRecyclerViewAdapter.submitList(hybridFileParcelables);
- searchRecyclerViewAdapter.notifyDataSetChanged();
+ mainActivity
+ .getCurrentMainFragment()
+ .getMainActivityViewModel()
+ .indexedSearch(mainActivity, s)
+ .observe(
+ mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
+ hybridFileParcelables -> {
+ searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+ searchRecyclerViewAdapter.notifyDataSetChanged();
+ });
searchMode = 2;
deepSearchTV.setText(
@@ -230,7 +236,7 @@ private void basicSearch(String s) {
mainActivity
.getCurrentMainFragment()
.getMainActivityViewModel()
- .basicSearch(s, mainActivity)
+ .basicSearch(mainActivity, s)
.observe(
mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
hybridFileParcelables -> {
@@ -305,41 +311,6 @@ private void initRecentSearches(Context context) {
}
}
- private List indexedSearch(MainActivity mainActivity, String query) {
-
- ArrayList list = new ArrayList<>();
- final String[] projection = {MediaStore.Files.FileColumns.DATA};
-
- Cursor cursor =
- mainActivity
- .getContentResolver()
- .query(MediaStore.Files.getContentUri("external"), projection, null, null, null);
-
- if (cursor == null) return list;
- else if (cursor.getCount() > 0 && cursor.moveToFirst()) {
- do {
- String path =
- cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
-
- if (path != null
- && path.contains(mainActivity.getCurrentMainFragment().getPath())
- && new File(path).getName().toLowerCase().contains(query.toLowerCase())) {
-
- boolean showHiddenFiles =
- PreferenceManager.getDefaultSharedPreferences(mainActivity)
- .getBoolean(PREFERENCE_SHOW_HIDDENFILES, false);
-
- HybridFileParcelable hybridFileParcelable =
- RootHelper.generateBaseFile(new File(path), showHiddenFiles);
-
- if (hybridFileParcelable != null) list.add(hybridFileParcelable);
- }
- } while (cursor.moveToNext());
- }
- cursor.close();
- return list;
- }
-
/** show search view with a circular reveal animation */
public void revealSearchView() {
final int START_RADIUS = 16;
From febaf038d2ab8d138fd2830729454a976bd35942 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 6 Jun 2023 23:22:04 +0530
Subject: [PATCH 026/328] chore: spotless
Signed-off-by: VishnuSanal
---
.../ui/activities/MainActivityViewModel.kt | 13 ++++-----
.../ui/views/appbar/SearchView.java | 27 +++++++------------
2 files changed, 15 insertions(+), 25 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
index 103aa2a643..eb90938ae4 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
@@ -116,9 +116,8 @@ class MainActivityViewModel(val applicationContext: Application) :
fun indexedSearch(
mainActivity: MainActivity,
- query: String,
- ): MutableLiveData< ArrayList > {
-
+ query: String
+ ): MutableLiveData> {
val list = ArrayList()
val mutableLiveData: MutableLiveData> = MutableLiveData(list)
@@ -128,7 +127,6 @@ class MainActivityViewModel(val applicationContext: Application) :
.getBoolean(PREFERENCE_SHOW_HIDDENFILES, false)
viewModelScope.launch(Dispatchers.IO) {
-
val projection = arrayOf(MediaStore.Files.FileColumns.DATA)
val cursor = mainActivity
@@ -141,13 +139,12 @@ class MainActivityViewModel(val applicationContext: Application) :
val path =
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
- if (path != null
- && path.contains(mainActivity.currentMainFragment?.currentPath!!)
- && File(path).name.lowercase(Locale.getDefault()).contains(
+ if (path != null &&
+ path.contains(mainActivity.currentMainFragment?.currentPath!!) &&
+ File(path).name.lowercase(Locale.getDefault()).contains(
query.lowercase(Locale.getDefault())
)
) {
-
val hybridFileParcelable =
RootHelper.generateBaseFile(File(path), showHiddenFiles)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 4a513b2825..b6ba9910a9 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -22,16 +22,11 @@
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
-import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES;
-import java.io.File;
import java.util.ArrayList;
-import java.util.List;
import com.amaze.filemanager.R;
import com.amaze.filemanager.adapters.SearchRecyclerViewAdapter;
-import com.amaze.filemanager.filesystem.HybridFileParcelable;
-import com.amaze.filemanager.filesystem.RootHelper;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
import com.amaze.filemanager.ui.theme.AppTheme;
@@ -45,9 +40,7 @@
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
-import android.database.Cursor;
import android.graphics.PorterDuff;
-import android.provider.MediaStore;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.ContextThemeWrapper;
@@ -172,16 +165,16 @@ public void afterTextChanged(Editable s) {}
if (searchMode == 1) {
- mainActivity
- .getCurrentMainFragment()
- .getMainActivityViewModel()
- .indexedSearch(mainActivity, s)
- .observe(
- mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
- hybridFileParcelables -> {
- searchRecyclerViewAdapter.submitList(hybridFileParcelables);
- searchRecyclerViewAdapter.notifyDataSetChanged();
- });
+ mainActivity
+ .getCurrentMainFragment()
+ .getMainActivityViewModel()
+ .indexedSearch(mainActivity, s)
+ .observe(
+ mainActivity.getCurrentMainFragment().getViewLifecycleOwner(),
+ hybridFileParcelables -> {
+ searchRecyclerViewAdapter.submitList(hybridFileParcelables);
+ searchRecyclerViewAdapter.notifyDataSetChanged();
+ });
searchMode = 2;
deepSearchTV.setText(
From 1cb7fd48a293f1306d46041ddf4c7889ee2e6b1f Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 6 Jun 2023 23:24:04 +0530
Subject: [PATCH 027/328] chore: spotless
Signed-off-by: VishnuSanal
---
.../ui/activities/MainActivityViewModel.kt | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
index eb90938ae4..32aecb7282 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
@@ -83,10 +83,13 @@ class MainActivityViewModel(val applicationContext: Application) :
return mediaCacheHash[mediaType]
}
- fun basicSearch(mainActivity: MainActivity, query: String): MutableLiveData> {
+ fun basicSearch(mainActivity: MainActivity, query: String):
+ MutableLiveData> {
val hybridFileParcelables = ArrayList()
- val mutableLiveData: MutableLiveData> = MutableLiveData(hybridFileParcelables)
+ val mutableLiveData:
+ MutableLiveData> =
+ MutableLiveData(hybridFileParcelables)
val showHiddenFiles = PreferenceManager
.getDefaultSharedPreferences(mainActivity)
@@ -100,8 +103,8 @@ class MainActivityViewModel(val applicationContext: Application) :
{ _: OpenMode? -> null }
) { hybridFileParcelable: HybridFileParcelable ->
if (hybridFileParcelable.getName(mainActivity)
- .lowercase(Locale.getDefault())
- .contains(query.lowercase(Locale.getDefault())) &&
+ .lowercase(Locale.getDefault())
+ .contains(query.lowercase(Locale.getDefault())) &&
(showHiddenFiles || !hybridFileParcelable.isHidden)
) {
hybridFileParcelables.add(hybridFileParcelable)
@@ -120,7 +123,9 @@ class MainActivityViewModel(val applicationContext: Application) :
): MutableLiveData> {
val list = ArrayList()
- val mutableLiveData: MutableLiveData> = MutableLiveData(list)
+ val mutableLiveData: MutableLiveData> = MutableLiveData(
+ list
+ )
val showHiddenFiles =
PreferenceManager.getDefaultSharedPreferences(mainActivity)
@@ -137,13 +142,15 @@ class MainActivityViewModel(val applicationContext: Application) :
if (cursor.count > 0 && cursor.moveToFirst()) {
do {
val path =
- cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
+ cursor.getString(
+ cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)
+ )
if (path != null &&
path.contains(mainActivity.currentMainFragment?.currentPath!!) &&
File(path).name.lowercase(Locale.getDefault()).contains(
- query.lowercase(Locale.getDefault())
- )
+ query.lowercase(Locale.getDefault())
+ )
) {
val hybridFileParcelable =
RootHelper.generateBaseFile(File(path), showHiddenFiles)
From 402934a6fb6defacab4e6f76c76850c1594710ba Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 7 Jun 2023 21:14:56 +0530
Subject: [PATCH 028/328] minor change to app manager UI
Signed-off-by: VishnuSanal
---
.../java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt | 1 -
.../java/com/amaze/filemanager/adapters/holders/AppHolder.kt | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt
index fa18dc62db..60460b7c2b 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/AppsRecyclerAdapter.kt
@@ -68,7 +68,6 @@ import com.amaze.filemanager.utils.AnimUtils.marqueeAfterDelay
import com.amaze.filemanager.utils.Utils
import com.amaze.filemanager.utils.safeLet
import java.io.File
-import kotlin.collections.ArrayList
import kotlin.math.roundToInt
class AppsRecyclerAdapter(
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt b/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
index b14091a4c0..97bbd696b1 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
@@ -60,7 +60,7 @@ class AppHolder(view: View) : RecyclerView.ViewHolder(view) {
packageName.visibility = View.VISIBLE
val layoutParams = txtDesc.layoutParams as ViewGroup.MarginLayoutParams
- layoutParams.setMargins(txtDesc.marginLeft, txtDesc.marginTop, 8, txtDesc.marginBottom)
+ layoutParams.setMargins(txtDesc.marginLeft, txtDesc.marginTop, 4, txtDesc.marginBottom)
txtDesc.layoutParams = layoutParams
view.findViewById(R.id.picture_icon).visibility = View.GONE
From adbeec0475f6b74eef5074026d37099c956336ce Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 7 Jun 2023 23:46:13 +0530
Subject: [PATCH 029/328] codacy: functions missing docs
Signed-off-by: VishnuSanal
---
.../filemanager/ui/activities/MainActivityViewModel.kt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
index 32aecb7282..81192a0d18 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt
@@ -83,6 +83,9 @@ class MainActivityViewModel(val applicationContext: Application) :
return mediaCacheHash[mediaType]
}
+ /**
+ * Perform basic search: searches on the current directory
+ */
fun basicSearch(mainActivity: MainActivity, query: String):
MutableLiveData> {
val hybridFileParcelables = ArrayList()
@@ -117,6 +120,9 @@ class MainActivityViewModel(val applicationContext: Application) :
return mutableLiveData
}
+ /**
+ * Perform indexed search: on MediaStore items from the current directory & it's children
+ */
fun indexedSearch(
mainActivity: MainActivity,
query: String
From 3e7c53db85eb47dc9397c9bf8493023cf175590e Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 7 Jun 2023 23:52:33 +0530
Subject: [PATCH 030/328] fix: glitch when searching for the second time
Signed-off-by: VishnuSanal
---
.../ui/views/appbar/SearchView.java | 20 +++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index b6ba9910a9..8664f1034c 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -107,14 +107,6 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
deepSearchTV = mainActivity.findViewById(R.id.searchDeepSearchTV);
recyclerView = mainActivity.findViewById(R.id.searchRecyclerView);
- searchMode = 0;
- deepSearchTV.setText(
- String.format(
- "%s %s",
- mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
- mainActivity.getString(R.string.try_indexed_search)));
- deepSearchTV.setVisibility(View.GONE);
-
initRecentSearches(mainActivity);
searchRecyclerViewAdapter = new SearchRecyclerViewAdapter();
@@ -304,11 +296,23 @@ private void initRecentSearches(Context context) {
}
}
+ private void resetSearchMode() {
+ searchMode = 0;
+ deepSearchTV.setText(
+ String.format(
+ "%s %s",
+ mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
+ mainActivity.getString(R.string.try_indexed_search)));
+ deepSearchTV.setVisibility(View.GONE);
+ }
+
/** show search view with a circular reveal animation */
public void revealSearchView() {
final int START_RADIUS = 16;
int endRadius = Math.max(appbar.getToolbar().getWidth(), appbar.getToolbar().getHeight());
+ resetSearchMode();
+
Animator animator;
if (SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
int[] searchCoords = new int[2];
From 0b0306e76b8c80fe078dd2463da2bee6b438df5b Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Thu, 8 Jun 2023 23:08:31 +0530
Subject: [PATCH 031/328] search: change "recursive" to "deep"
Signed-off-by: VishnuSanal
---
.../com/amaze/filemanager/ui/views/appbar/SearchView.java | 8 ++++----
app/src/main/res/values/strings.xml | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 8664f1034c..b3763b29c3 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -83,9 +83,9 @@ public class SearchView {
private final SearchRecyclerViewAdapter searchRecyclerViewAdapter;
- // 0 -> Basic
- // 1 -> Indexed
- // 2 -> Recursive
+ // 0 -> Basic Search
+ // 1 -> Indexed Search
+ // 2 -> Deep Search
private int searchMode;
private boolean enabled = false;
@@ -173,7 +173,7 @@ public void afterTextChanged(Editable s) {}
String.format(
"%s %s",
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
- mainActivity.getString(R.string.try_recursive_search)));
+ mainActivity.getString(R.string.try_deep_search)));
} else if (searchMode == 2) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0dcc2662e7..8e71c95b5a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -810,7 +810,7 @@ You only need to do this once, until the next time you select a new location for
Cloud Connection credentials cleared
Unfortunately, we were unable to migrate your cloud connection credentials to the new database schema, and we had to remove them from the app. Please create the cloud connection again.
Not finding what you\'re looking for?
- Try Recursive Search!
+ Try Deep Search!
Try Indexed Search!
Recent
Results
From e48a7808d1f0173ed5fd9751aa7dfd901350429b Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Thu, 8 Jun 2023 23:28:14 +0530
Subject: [PATCH 032/328] fix crash (seems like the issue was caused by not
clearing the RecyclerView when a new dataset needs to be listed)
(https://stackoverflow.com/q/35653439/9652621)
Signed-off-by: VishnuSanal
---
.../java/com/amaze/filemanager/ui/views/appbar/SearchView.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index b3763b29c3..0f12d79c13 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -112,8 +112,6 @@ public SearchView(final AppBar appbar, MainActivity mainActivity, SearchListener
searchRecyclerViewAdapter = new SearchRecyclerViewAdapter();
recyclerView.setAdapter(searchRecyclerViewAdapter);
- clearRecyclerView();
-
clearImageView.setOnClickListener(
v -> {
searchViewEditText.setText("");
@@ -312,6 +310,7 @@ public void revealSearchView() {
int endRadius = Math.max(appbar.getToolbar().getWidth(), appbar.getToolbar().getHeight());
resetSearchMode();
+ clearRecyclerView();
Animator animator;
if (SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
From b8f5c4ae80c5a1f53bade8a2ebf5976625834663 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Mon, 12 Jun 2023 00:19:51 +0530
Subject: [PATCH 033/328] move deep search TV to top
Signed-off-by: VishnuSanal
---
app/src/main/res/layout-v21/layout_search.xml | 42 +++++++++---------
.../main/res/layout-w720dp/layout_search.xml | 43 ++++++++++---------
app/src/main/res/layout/layout_search.xml | 42 +++++++++---------
3 files changed, 66 insertions(+), 61 deletions(-)
diff --git a/app/src/main/res/layout-v21/layout_search.xml b/app/src/main/res/layout-v21/layout_search.xml
index ed8aaa8dcb..b5ce1cdc1b 100644
--- a/app/src/main/res/layout-v21/layout_search.xml
+++ b/app/src/main/res/layout-v21/layout_search.xml
@@ -81,9 +81,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
- android:padding="8dp"
+ android:paddingStart="8dp"
+ android:paddingTop="8dp"
+ android:paddingEnd="8dp"
android:scrollbars="none"
- app:layout_constraintBottom_toTopOf="@id/searchResultsHintTV"
+ app:layout_constraintBottom_toTopOf="@id/searchDeepSearchTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchRecentHintTV">
@@ -101,6 +103,22 @@
+
+
+ app:layout_constraintTop_toBottomOf="@id/searchDeepSearchTV" />
-
-
+ app:layout_constraintTop_toBottomOf="@id/searchResultsHintTV" />
\ No newline at end of file
diff --git a/app/src/main/res/layout-w720dp/layout_search.xml b/app/src/main/res/layout-w720dp/layout_search.xml
index e7360f67db..177e166eb1 100644
--- a/app/src/main/res/layout-w720dp/layout_search.xml
+++ b/app/src/main/res/layout-w720dp/layout_search.xml
@@ -78,9 +78,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
- android:padding="8dp"
+ android:paddingStart="8dp"
+ android:paddingTop="8dp"
+ android:paddingEnd="8dp"
android:scrollbars="none"
- app:layout_constraintBottom_toTopOf="@id/searchResultsHintTV"
+ app:layout_constraintBottom_toTopOf="@id/searchDeepSearchTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchRecentHintTV">
@@ -98,6 +100,22 @@
+
+
-
+ app:layout_constraintTop_toBottomOf="@id/searchDeepSearchTV" />
-
-
+ app:layout_constraintTop_toBottomOf="@id/searchResultsHintTV" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_search.xml b/app/src/main/res/layout/layout_search.xml
index f6c646f9d2..650b33bca0 100644
--- a/app/src/main/res/layout/layout_search.xml
+++ b/app/src/main/res/layout/layout_search.xml
@@ -78,9 +78,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
- android:padding="8dp"
+ android:paddingStart="8dp"
+ android:paddingTop="8dp"
+ android:paddingEnd="8dp"
android:scrollbars="none"
- app:layout_constraintBottom_toTopOf="@id/searchResultsHintTV"
+ app:layout_constraintBottom_toTopOf="@id/searchDeepSearchTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchRecentHintTV">
@@ -98,6 +100,22 @@
+
+
+ app:layout_constraintTop_toBottomOf="@id/searchDeepSearchTV" />
-
-
+ app:layout_constraintTop_toBottomOf="@id/searchResultsHintTV" />
\ No newline at end of file
From f41e169f22a80e3a6313e9a18b59639a8ec06691 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Mon, 12 Jun 2023 00:34:52 +0530
Subject: [PATCH 034/328] apply accent color to deep search prompt
Signed-off-by: VishnuSanal
---
.../ui/views/appbar/SearchView.java | 35 +++++++++++++++----
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 0f12d79c13..794c560025 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -41,8 +41,13 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PorterDuff;
+import android.graphics.Typeface;
import android.text.Editable;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextWatcher;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -167,9 +172,9 @@ public void afterTextChanged(Editable s) {}
});
searchMode = 2;
+
deepSearchTV.setText(
- String.format(
- "%s %s",
+ getSpannableText(
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_deep_search)));
@@ -211,8 +216,7 @@ private void basicSearch(String s) {
deepSearchTV.setVisibility(View.VISIBLE);
searchMode = 1;
deepSearchTV.setText(
- String.format(
- "%s %s",
+ getSpannableText(
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_indexed_search)));
@@ -297,8 +301,7 @@ private void initRecentSearches(Context context) {
private void resetSearchMode() {
searchMode = 0;
deepSearchTV.setText(
- String.format(
- "%s %s",
+ getSpannableText(
mainActivity.getString(R.string.not_finding_what_you_re_looking_for),
mainActivity.getString(R.string.try_indexed_search)));
deepSearchTV.setVisibility(View.GONE);
@@ -310,7 +313,7 @@ public void revealSearchView() {
int endRadius = Math.max(appbar.getToolbar().getWidth(), appbar.getToolbar().getHeight());
resetSearchMode();
- clearRecyclerView();
+ clearRecyclerView();
Animator animator;
if (SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
@@ -461,6 +464,24 @@ private void clearRecyclerView() {
searchResultsHintTV.setVisibility(View.GONE);
}
+ private SpannableString getSpannableText(String s1, String s2) {
+
+ SpannableString spannableString = new SpannableString(s1 + " " + s2);
+
+ spannableString.setSpan(
+ new ForegroundColorSpan(mainActivity.getCurrentColorPreference().getAccent()),
+ s1.length() + 1,
+ spannableString.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ spannableString.setSpan(
+ new StyleSpan(Typeface.BOLD),
+ s1.length() + 1,
+ spannableString.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ return spannableString;
+ }
+
public interface SearchListener {
void onSearch(String queue);
}
From e0a052ad78a6465464e77d1a03140ccb25451d91 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 13 Jun 2023 21:14:47 +0530
Subject: [PATCH 035/328] add to recents upon indexed search
Signed-off-by: VishnuSanal
---
.../java/com/amaze/filemanager/ui/views/appbar/SearchView.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
index 794c560025..bbe3a77ac7 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java
@@ -160,6 +160,8 @@ public void afterTextChanged(Editable s) {}
if (searchMode == 1) {
+ saveRecentPreference(s);
+
mainActivity
.getCurrentMainFragment()
.getMainActivityViewModel()
From df0eaadc0c4cf74c8fec6289e46f45c2bd08ddc3 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Tue, 13 Jun 2023 21:20:28 +0530
Subject: [PATCH 036/328] use the color view on the left to distinguish between
a file & a directory
Signed-off-by: VishnuSanal
---
.../adapters/SearchRecyclerViewAdapter.kt | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
index ad17c6caa9..c523453385 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -69,14 +69,14 @@ class SearchRecyclerViewAdapter :
holder.colorView.setBackgroundColor(getRandomColor(holder.colorView.context))
-// val colorPreference =
-// (AppConfig.getInstance().mainActivityContext as MainActivity).currentColorPreference
-//
-// if (item != null && item.isDirectory) { // always false for some reason!
-// holder.colorView.setBackgroundColor(colorPreference.iconSkin)
-// } else {
-// holder.colorView.setBackgroundColor(colorPreference.accent)
-// }
+ val colorPreference =
+ (AppConfig.getInstance().mainActivityContext as MainActivity).currentColorPreference
+
+ if (item.isDirectory) {
+ holder.colorView.setBackgroundColor(colorPreference.primaryFirstTab)
+ } else {
+ holder.colorView.setBackgroundColor(colorPreference.accent)
+ }
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
From f23522e9b82d01f3649e41a81ab3847aaf7e9074 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 14 Jun 2023 22:16:32 +0530
Subject: [PATCH 037/328] return the actual path for local files
Signed-off-by: VishnuSanal
---
.../java/com/amaze/filemanager/filesystem/HybridFile.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
index 8f55be3882..8e69b2d97d 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
@@ -416,11 +416,15 @@ public Long execute(@NonNull SFTPClient client) throws IOException {
}
/**
- * Path accessor. Avoid direct access to path since path may have been URL encoded.
+ * Path accessor. Avoid direct access to path (for non-local files) since path may have been URL
+ * encoded.
*
- * @return URL decoded path
+ * @return URL decoded path (for non-local files); the actual path for local files
*/
public String getPath() {
+
+ if (isLocal() || isRoot() || isDocumentFile() || isAndroidDataDir()) return path;
+
try {
return URLDecoder.decode(path.replace("+", "%2b"), "UTF-8");
} catch (UnsupportedEncodingException | IllegalArgumentException e) {
From f11f6a9d1f21a2b5451a2532ecef4db1e1886c90 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Wed, 14 Jun 2023 22:24:01 +0530
Subject: [PATCH 038/328] remove the workaround done to replace plus symbol
(via #3661)
Signed-off-by: VishnuSanal
---
.../main/java/com/amaze/filemanager/filesystem/HybridFile.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
index 8e69b2d97d..13d9c40bf8 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java
@@ -426,7 +426,7 @@ public String getPath() {
if (isLocal() || isRoot() || isDocumentFile() || isAndroidDataDir()) return path;
try {
- return URLDecoder.decode(path.replace("+", "%2b"), "UTF-8");
+ return URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException | IllegalArgumentException e) {
LOG.warn("failed to decode path {}", path, e);
return path;
From 38f795422a1dd8c0a5ecd40e6a3452592041f2d3 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Thu, 15 Jun 2023 23:29:27 +0530
Subject: [PATCH 039/328] fix broken media scanner
Signed-off-by: VishnuSanal
---
.../asynchronous/asynctasks/DeleteTask.java | 24 +-----
.../asynctasks/movecopy/MoveFilesTask.kt | 6 +-
.../asynchronous/services/CopyService.java | 3 +-
.../filemanager/filesystem/Operations.java | 3 +-
.../filesystem/files/FileUtils.java | 76 -------------------
.../filesystem/files/GenericCopyUtil.java | 2 +-
.../filesystem/files/MediaConnectionUtils.kt | 53 +++++++++++++
.../ui/fragments/MainFragment.java | 3 +-
8 files changed, 67 insertions(+), 103 deletions(-)
create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/files/MediaConnectionUtils.kt
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java
index 15ec67cace..c63bbe3ef6 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/DeleteTask.java
@@ -38,7 +38,7 @@
import com.amaze.filemanager.filesystem.SafRootHolder;
import com.amaze.filemanager.filesystem.cloud.CloudUtil;
import com.amaze.filemanager.filesystem.files.CryptUtil;
-import com.amaze.filemanager.filesystem.files.FileUtils;
+import com.amaze.filemanager.filesystem.files.MediaConnectionUtils;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.fragments.CompressedExplorerFragment;
import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
@@ -48,12 +48,9 @@
import com.cloudrail.si.interfaces.CloudStorage;
import android.app.NotificationManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
import android.os.AsyncTask;
-import android.provider.MediaStore;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -112,13 +109,9 @@ protected final AsyncTaskResult doInBackground(
}
// delete file from media database
- if (!file.isSmb()) {
- try {
- deleteFromMediaDatabase(applicationContext, file.getPath());
- } catch (Exception e) {
- FileUtils.scanFile(applicationContext, files.toArray(new HybridFile[files.size()]));
- }
- }
+ if (!file.isSmb())
+ MediaConnectionUtils.scanFile(
+ applicationContext, files.toArray(new HybridFile[files.size()]));
// delete file entry from encrypted database
if (file.getName(applicationContext).endsWith(CryptUtil.CRYPT_EXTENSION)) {
@@ -194,13 +187,4 @@ private boolean doDeleteFile(@NonNull HybridFileParcelable file) throws Exceptio
}
}
}
-
- private void deleteFromMediaDatabase(final Context context, final String file) {
- final String where = MediaStore.MediaColumns.DATA + "=?";
- final String[] selectionArgs = new String[] {file};
- final ContentResolver contentResolver = context.getContentResolver();
- final Uri filesUri = MediaStore.Files.getContentUri("external");
- // Delete the entry from the media database. This will actually delete media files.
- contentResolver.delete(filesUri, where, selectionArgs);
- }
}
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFilesTask.kt b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFilesTask.kt
index 196f05d668..988cfb8bde 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFilesTask.kt
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/asynctasks/movecopy/MoveFilesTask.kt
@@ -34,7 +34,7 @@ import com.amaze.filemanager.fileoperations.filesystem.OpenMode
import com.amaze.filemanager.filesystem.HybridFile
import com.amaze.filemanager.filesystem.HybridFileParcelable
import com.amaze.filemanager.filesystem.files.CryptUtil
-import com.amaze.filemanager.filesystem.files.FileUtils
+import com.amaze.filemanager.filesystem.files.MediaConnectionUtils
import com.amaze.filemanager.ui.activities.MainActivity
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -106,8 +106,8 @@ class MoveFilesTask(
for (hybridFileParcelables in files) {
sourcesFiles.addAll(hybridFileParcelables)
}
- FileUtils.scanFile(applicationContext, sourcesFiles.toTypedArray())
- FileUtils.scanFile(applicationContext, targetFiles.toTypedArray())
+ MediaConnectionUtils.scanFile(applicationContext, sourcesFiles.toTypedArray())
+ MediaConnectionUtils.scanFile(applicationContext, targetFiles.toTypedArray())
}
// updating encrypted db entry if any encrypted file was moved
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java b/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java
index e83fdd4af7..4ca5b3eeec 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java
@@ -43,6 +43,7 @@
import com.amaze.filemanager.filesystem.files.CryptUtil;
import com.amaze.filemanager.filesystem.files.FileUtils;
import com.amaze.filemanager.filesystem.files.GenericCopyUtil;
+import com.amaze.filemanager.filesystem.files.MediaConnectionUtils;
import com.amaze.filemanager.filesystem.root.CopyFilesCommand;
import com.amaze.filemanager.filesystem.root.MoveFileCommand;
import com.amaze.filemanager.ui.activities.MainActivity;
@@ -467,7 +468,7 @@ void copyRoot(HybridFileParcelable sourceFile, HybridFile targetFile, boolean mo
e);
failedFOps.add(sourceFile);
}
- FileUtils.scanFile(c, new HybridFile[] {targetFile});
+ MediaConnectionUtils.scanFile(c, new HybridFile[] {targetFile});
}
private void copyFiles(
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java
index 4125f726eb..6786b1efa2 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/Operations.java
@@ -41,6 +41,7 @@
import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.filesystem.cloud.CloudUtil;
import com.amaze.filemanager.filesystem.files.FileUtils;
+import com.amaze.filemanager.filesystem.files.MediaConnectionUtils;
import com.amaze.filemanager.filesystem.ftp.FtpClientTemplate;
import com.amaze.filemanager.filesystem.ftp.NetCopyClientUtils;
import com.amaze.filemanager.filesystem.root.MakeDirectoryCommand;
@@ -715,7 +716,7 @@ protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (newFile != null && oldFile != null) {
HybridFile[] hybridFiles = {newFile, oldFile};
- FileUtils.scanFile(context, hybridFiles);
+ MediaConnectionUtils.scanFile(context, hybridFiles);
}
}
}.executeOnExecutor(executor);
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java
index d55e7f09b3..4fcf97cb81 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/FileUtils.java
@@ -30,7 +30,6 @@
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
@@ -42,7 +41,6 @@
import com.amaze.filemanager.application.AppConfig;
import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.fileoperations.filesystem.smbstreamer.Streamer;
-import com.amaze.filemanager.filesystem.ExternalSdCardOperation;
import com.amaze.filemanager.filesystem.HybridFile;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
import com.amaze.filemanager.filesystem.Operations;
@@ -71,7 +69,6 @@
import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -79,7 +76,6 @@
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -92,8 +88,6 @@
import androidx.core.util.Pair;
import androidx.documentfile.provider.DocumentFile;
-import io.reactivex.Flowable;
-import io.reactivex.schedulers.Schedulers;
import jcifs.smb.SmbFile;
import kotlin.collections.ArraysKt;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
@@ -217,76 +211,6 @@ public static long getBaseFileSize(HybridFileParcelable baseFile, Context contex
}
}
- /**
- * Triggers media scanner for multiple paths. The paths must all belong to same filesystem. It's
- * upto the caller to call the mediastore scan on multiple files or only one source/target
- * directory. Don't use filesystem API directly as files might not be present anymore (eg.
- * move/rename) which may lead to {@link java.io.FileNotFoundException}
- *
- * @param hybridFiles
- * @param context
- */
- @SuppressLint("CheckResult")
- public static void scanFile(@NonNull Context context, @NonNull HybridFile[] hybridFiles) {
- Flowable.fromCallable(
- (Callable)
- () -> {
- if (hybridFiles[0].exists(context) && hybridFiles[0].isLocal()) {
- String[] paths = new String[hybridFiles.length];
- for (int i = 0; i < hybridFiles.length; i++) {
- HybridFile hybridFile = hybridFiles[i];
- paths[i] = hybridFile.getPath();
- }
- MediaScannerConnection.scanFile(context, paths, null, null);
- }
- for (HybridFile hybridFile : hybridFiles) {
- scanFile(hybridFile, context);
- }
- return null;
- })
- .subscribeOn(Schedulers.io());
- }
-
- /**
- * Triggers media store for the file path
- *
- * @param hybridFile the file which was changed (directory not supported)
- * @param context given context
- */
- private static void scanFile(@NonNull HybridFile hybridFile, Context context) {
-
- if ((hybridFile.isLocal() || hybridFile.isOtgFile()) && hybridFile.exists(context)) {
-
- Uri uri = null;
- if (Build.VERSION.SDK_INT >= 19) {
- DocumentFile documentFile =
- ExternalSdCardOperation.getDocumentFile(
- hybridFile.getFile(), hybridFile.isDirectory(context), context);
- // If FileUtil.getDocumentFile() returns null, fall back to DocumentFile.fromFile()
- if (documentFile == null) documentFile = DocumentFile.fromFile(hybridFile.getFile());
- uri = documentFile.getUri();
- } else {
- if (hybridFile.isLocal()) {
- uri = Uri.fromFile(hybridFile.getFile());
- }
- }
- if (uri != null) {
- FileUtils.scanFile(uri, context);
- }
- }
- }
-
- /**
- * Triggers {@link Intent#ACTION_MEDIA_SCANNER_SCAN_FILE} intent to refresh the media store.
- *
- * @param uri File's {@link Uri}
- * @param c {@link Context}
- */
- private static void scanFile(@NonNull Uri uri, @NonNull Context c) {
- Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
- c.sendBroadcast(mediaScanIntent);
- }
-
public static void crossfade(View buttons, final View pathbar) {
// Set the content view to 0% opacity but visible, so that it is visible
// (but fully transparent) during the animation.
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java
index 068d6a95fe..0f09d9cc49 100644
--- a/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/GenericCopyUtil.java
@@ -271,7 +271,7 @@ private void startCopy(
// If target file is copied onto the device and copy was successful, trigger media store
// rescan
if (mTargetFile != null) {
- FileUtils.scanFile(mContext, new HybridFile[] {mTargetFile});
+ MediaConnectionUtils.scanFile(mContext, new HybridFile[] {mTargetFile});
}
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/MediaConnectionUtils.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/MediaConnectionUtils.kt
new file mode 100644
index 0000000000..dadb5744e3
--- /dev/null
+++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/MediaConnectionUtils.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014-2022 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Manager.
+ *
+ * Amaze File Manager is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.amaze.filemanager.filesystem.files
+
+import android.content.Context
+import android.media.MediaScannerConnection
+import android.net.Uri
+import android.util.Log
+import com.amaze.filemanager.filesystem.HybridFile
+
+object MediaConnectionUtils {
+ private val TAG = MediaConnectionUtils::class.java.simpleName
+
+ /**
+ * Invokes MediaScannerConnection#scanFile for the given files
+ *
+ * @param context the context
+ * @param hybridFiles files to be scanned
+ */
+ @JvmStatic
+ fun scanFile(context: Context, hybridFiles: Array) {
+ val paths = arrayOfNulls(hybridFiles.size)
+
+ for (i in hybridFiles.indices) paths[i] = hybridFiles[i].path
+
+ MediaScannerConnection.scanFile(
+ context,
+ paths,
+ null
+ ) {
+ path: String, _: Uri? ->
+ Log.i(TAG, "public scanFile() Finished scanning path$path")
+ }
+ }
+}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java
index 5abc495d5e..c74b9cd7e9 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java
@@ -63,6 +63,7 @@
import com.amaze.filemanager.filesystem.files.EncryptDecryptUtils;
import com.amaze.filemanager.filesystem.files.FileListSorter;
import com.amaze.filemanager.filesystem.files.FileUtils;
+import com.amaze.filemanager.filesystem.files.MediaConnectionUtils;
import com.amaze.filemanager.ui.ExtensionsKt;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.activities.MainActivityViewModel;
@@ -1401,7 +1402,7 @@ public void hide(String path) {
LOG.warn("failure when hiding file", e);
}
}
- FileUtils.scanFile(
+ MediaConnectionUtils.scanFile(
requireMainActivity(), new HybridFile[] {new HybridFile(OpenMode.FILE, path)});
}
}
From 6f52b0c515561a17959868f234afa2b3792c6e02 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sat, 24 Sep 2022 17:39:48 +0800
Subject: [PATCH 040/328] Subnetscanner v2
Fixes #2622 and fixes #3386.
---
.../filemanager/ui/dialogs/SmbSearchDialog.kt | 15 ++++++++++++---
.../utils/smb/SameSubnetDiscoverDeviceStrategy.kt | 11 ++++++++++-
.../utils/smb/SmbDeviceScannerObservable.kt | 6 +++++-
.../filemanager/ui/activities/MainActivityTest.kt | 4 ++--
.../ui/dialogs/SmbConnectDialogTest.kt | 2 +-
portscanner/src/main/AndroidManifest.xml | 8 +-------
6 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
index bd92520db8..014f5b5d3b 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
@@ -152,7 +152,12 @@ class SmbSearchDialog : DialogFragment() {
* Called by [ComputerParcelableViewModel], add found computer to list view
*/
fun add(computer: ComputerParcelable) {
- items.add(computer)
+ if (computer.addr == "-1" && computer.name == "-1") {
+ items.add(computer)
+ } else {
+ items.add(items.size - 1, computer)
+ removeDummy()
+ }
notifyDataSetChanged()
}
@@ -161,7 +166,11 @@ class SmbSearchDialog : DialogFragment() {
* (dummy) host
*/
fun removeDummy() {
- items.removeFirst()
+ items.remove(
+ items.find {
+ it.addr == "-1" && it.name == "-1"
+ }
+ )
notifyDataSetChanged()
}
@@ -176,7 +185,7 @@ class SmbSearchDialog : DialogFragment() {
* Answers if the list is empty = only has the dummy [ComputerParcelable] instance
*/
fun dummyOnly(): Boolean {
- return items.size == 1 && items.first().addr == "-1"
+ return items.size == 1 && items.last().addr == "-1"
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
diff --git a/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt
index 277150c647..d4385fe95a 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/SameSubnetDiscoverDeviceStrategy.kt
@@ -80,7 +80,16 @@ class SameSubnetDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDevi
it is InetAddress
}.doOnNext { addr ->
addr as InetAddress
- callback.invoke(ComputerParcelable(addr.hostAddress, addr.hostName))
+ callback.invoke(
+ ComputerParcelable(
+ addr.hostAddress,
+ if (addr.hostName == addr.hostAddress) {
+ addr.canonicalHostName
+ } else {
+ addr.hostName
+ }
+ )
+ )
}.sequential().subscribe()
}
diff --git a/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt
index 14dcced9d8..0d4afd52c4 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/SmbDeviceScannerObservable.kt
@@ -20,6 +20,7 @@
package com.amaze.filemanager.utils.smb
+import androidx.annotation.VisibleForTesting
import com.amaze.filemanager.utils.ComputerParcelable
import com.amaze.filemanager.utils.smb.SmbDeviceScannerObservable.DiscoverDeviceStrategy
import io.reactivex.Observable
@@ -50,11 +51,14 @@ class SmbDeviceScannerObservable : Observable() {
fun onCancel()
}
- private var discoverDeviceStrategies: Array =
+ var discoverDeviceStrategies: Array =
arrayOf(
WsddDiscoverDeviceStrategy(),
SameSubnetDiscoverDeviceStrategy()
)
+ @VisibleForTesting set
+
+ @VisibleForTesting get
private lateinit var observer: Observer
diff --git a/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.kt b/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.kt
index 3743edfbca..79aeb00fa5 100644
--- a/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.kt
+++ b/app/src/test/java/com/amaze/filemanager/ui/activities/MainActivityTest.kt
@@ -24,8 +24,8 @@ import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import com.amaze.filemanager.application.AppConfig
-import com.amaze.filemanager.utils.SmbUtil.getSmbDecryptedPath
-import com.amaze.filemanager.utils.SmbUtil.getSmbEncryptedPath
+import com.amaze.filemanager.utils.smb.SmbUtil.getSmbDecryptedPath
+import com.amaze.filemanager.utils.smb.SmbUtil.getSmbEncryptedPath
import org.awaitility.Awaitility.await
import org.junit.Assert.assertEquals
import org.junit.Test
diff --git a/app/src/test/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialogTest.kt b/app/src/test/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialogTest.kt
index e2d7a4be17..dd76331050 100644
--- a/app/src/test/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialogTest.kt
+++ b/app/src/test/java/com/amaze/filemanager/ui/dialogs/SmbConnectDialogTest.kt
@@ -32,7 +32,7 @@ import com.amaze.filemanager.ui.dialogs.SmbConnectDialog.ARG_EDIT
import com.amaze.filemanager.ui.dialogs.SmbConnectDialog.ARG_NAME
import com.amaze.filemanager.ui.dialogs.SmbConnectDialog.ARG_PATH
import com.amaze.filemanager.ui.dialogs.SmbConnectDialog.SmbConnectionListener
-import com.amaze.filemanager.utils.SmbUtil
+import com.amaze.filemanager.utils.smb.SmbUtil
import io.mockk.confirmVerified
import io.mockk.spyk
import io.mockk.verify
diff --git a/portscanner/src/main/AndroidManifest.xml b/portscanner/src/main/AndroidManifest.xml
index 100ab263c3..568741e54f 100644
--- a/portscanner/src/main/AndroidManifest.xml
+++ b/portscanner/src/main/AndroidManifest.xml
@@ -1,8 +1,2 @@
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
From e536f9f37a64204748087822f51e2ae92d7c7d23 Mon Sep 17 00:00:00 2001
From: VishnuSanal
Date: Fri, 23 Jun 2023 21:01:58 +0530
Subject: [PATCH 041/328] use Utils#dpToPx
Signed-off-by: VishnuSanal
---
.../com/amaze/filemanager/adapters/holders/AppHolder.kt | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt b/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
index 97bbd696b1..f5ef96a37f 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
@@ -32,6 +32,7 @@ import androidx.core.view.marginTop
import androidx.recyclerview.widget.RecyclerView
import com.amaze.filemanager.R
import com.amaze.filemanager.ui.views.ThemedTextView
+import com.amaze.filemanager.utils.Utils
class AppHolder(view: View) : RecyclerView.ViewHolder(view) {
@JvmField
@@ -60,7 +61,12 @@ class AppHolder(view: View) : RecyclerView.ViewHolder(view) {
packageName.visibility = View.VISIBLE
val layoutParams = txtDesc.layoutParams as ViewGroup.MarginLayoutParams
- layoutParams.setMargins(txtDesc.marginLeft, txtDesc.marginTop, 4, txtDesc.marginBottom)
+ layoutParams.setMargins(
+ txtDesc.marginLeft,
+ txtDesc.marginTop,
+ Utils.dpToPx(view.context, 4),
+ txtDesc.marginBottom
+ )
txtDesc.layoutParams = layoutParams
view.findViewById(R.id.picture_icon).visibility = View.GONE
From 6bd56f439e324ff60cd5eb5eb356dc1e52a86cd9 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sat, 24 Jun 2023 17:37:07 +0800
Subject: [PATCH 042/328] Adds Compress option to item popup menu
Fixes #3766.
---
.../com/amaze/filemanager/adapters/RecyclerAdapter.java | 5 ++++-
.../java/com/amaze/filemanager/ui/ItemPopupMenu.java | 6 ++++++
.../filemanager/ui/dialogs/GeneralDialogCreation.java | 9 +++++++++
app/src/main/res/menu/item_extras.xml | 3 +++
4 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java b/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java
index 047aadfba0..7e180188ae 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java
+++ b/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java
@@ -1392,6 +1392,7 @@ private void showPopup(@NonNull View view, @NonNull final LayoutElementParcelabl
}
} else {
popupMenu.getMenu().findItem(R.id.book).setVisible(false);
+ popupMenu.getMenu().findItem(R.id.compress).setVisible(true);
if (description.endsWith(fileExtensionZip)
|| description.endsWith(fileExtensionJar)
@@ -1409,8 +1410,10 @@ private void showPopup(@NonNull View view, @NonNull final LayoutElementParcelabl
|| description.endsWith(fileExtensionGz)
|| description.endsWith(fileExtensionBzip2)
|| description.endsWith(fileExtensionLzma)
- || description.endsWith(fileExtensionXz))
+ || description.endsWith(fileExtensionXz)) {
popupMenu.getMenu().findItem(R.id.ex).setVisible(true);
+ popupMenu.getMenu().findItem(R.id.compress).setVisible(false);
+ }
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
diff --git a/app/src/main/java/com/amaze/filemanager/ui/ItemPopupMenu.java b/app/src/main/java/com/amaze/filemanager/ui/ItemPopupMenu.java
index 1785f87d5a..a3ede65503 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/ItemPopupMenu.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/ItemPopupMenu.java
@@ -241,6 +241,12 @@ public void onButtonPressed(Intent intent, String password)
utilitiesProvider,
false);
return true;
+ case R.id.compress:
+ GeneralDialogCreation.showCompressDialog(
+ mainActivity,
+ rowItem.generateBaseFile(),
+ mainActivity.getCurrentMainFragment().getMainFragmentViewModel().getCurrentPath());
+ return true;
case R.id.return_select:
mainFragment.returnIntentResults(rowItem.generateBaseFile());
return true;
diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java
index c7e917c0a6..d952d3edde 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/GeneralDialogCreation.java
@@ -867,6 +867,15 @@ public static void showArchiveDialog(final File f, final MainActivity m) {
b.show();
}
+ public static void showCompressDialog(
+ @NonNull final MainActivity mainActivity,
+ final HybridFileParcelable baseFile,
+ final String current) {
+ ArrayList baseFiles = new ArrayList<>();
+ baseFiles.add(baseFile);
+ showCompressDialog(mainActivity, baseFiles, current);
+ }
+
public static void showCompressDialog(
@NonNull final MainActivity mainActivity,
final ArrayList baseFiles,
diff --git a/app/src/main/res/menu/item_extras.xml b/app/src/main/res/menu/item_extras.xml
index acc508b5dc..d2d2d1201e 100644
--- a/app/src/main/res/menu/item_extras.xml
+++ b/app/src/main/res/menu/item_extras.xml
@@ -39,6 +39,9 @@
+
From 5107b2ab9c98f37ab567aa266dac9bc54ac99c84 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Tue, 27 Jun 2023 23:09:38 +0800
Subject: [PATCH 043/328] Remove set drawer header background and Volley
dependency
We are used to allow setting the drawer's background image when we still had a Google+ community. Then we disabled it and the code was left cold and dead for quite a while.
This PR removes the dead code and Volley dependency altogether, paving the way for OkHttp as the HTTP client library to use within Amaze, as well as the blockade towards implementing WebDAV client support(#376)
---
app/build.gradle | 2 -
.../filemanager/application/AppConfig.java | 17 -----
.../ui/activities/MainActivity.java | 6 +-
.../filemanager/ui/views/drawer/Drawer.java | 52 ---------------
.../filemanager/utils/LruBitmapCache.java | 63 -------------------
.../application/AppConfigTest.java | 13 ----
6 files changed, 2 insertions(+), 151 deletions(-)
delete mode 100644 app/src/main/java/com/amaze/filemanager/utils/LruBitmapCache.java
diff --git a/app/build.gradle b/app/build.gradle
index 34cd3e4b97..ecb040a502 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -187,8 +187,6 @@ dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
- implementation 'com.android.volley:volley:1.2.1'
-
implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"
implementation "com.github.topjohnwu.libsu:io:${libsuVersion}"
diff --git a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java
index 09a33595f9..0c0c18379f 100644
--- a/app/src/main/java/com/amaze/filemanager/application/AppConfig.java
+++ b/app/src/main/java/com/amaze/filemanager/application/AppConfig.java
@@ -41,11 +41,7 @@
import com.amaze.filemanager.database.UtilsHandler;
import com.amaze.filemanager.filesystem.ssh.CustomSshJConfig;
import com.amaze.filemanager.ui.provider.UtilitiesProvider;
-import com.amaze.filemanager.utils.LruBitmapCache;
import com.amaze.filemanager.utils.ScreenUtils;
-import com.android.volley.RequestQueue;
-import com.android.volley.toolbox.ImageLoader;
-import com.android.volley.toolbox.Volley;
import android.app.Activity;
import android.app.Application;
@@ -71,8 +67,6 @@ public class AppConfig extends GlideApplication {
private Logger log = null;
private UtilitiesProvider utilsProvider;
- private RequestQueue requestQueue;
- private ImageLoader imageLoader;
private UtilsHandler utilsHandler;
private WeakReference mainActivityContext;
@@ -201,17 +195,6 @@ public static synchronized AppConfig getInstance() {
return instance;
}
- public ImageLoader getImageLoader() {
- if (requestQueue == null) {
- requestQueue = Volley.newRequestQueue(getApplicationContext());
- }
-
- if (imageLoader == null) {
- this.imageLoader = new ImageLoader(requestQueue, new LruBitmapCache());
- }
- return imageLoader;
- }
-
public UtilsHandler getUtilsHandler() {
return utilsHandler;
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java
index 04cd312614..d268f41ad1 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java
@@ -1539,9 +1539,7 @@ public Drawer getDrawer() {
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
super.onActivityResult(requestCode, responseCode, intent);
- if (requestCode == Drawer.image_selector_request_code) {
- drawer.onActivityResult(requestCode, responseCode, intent);
- } else if (requestCode == 3) {
+ if (requestCode == 3) {
Uri treeUri;
if (responseCode == Activity.RESULT_OK) {
// Get Uri from Storage Access Framework.
@@ -1721,7 +1719,7 @@ void initialiseViews() {
if (getAppbar().getSearchView().isEnabled()) getAppbar().getSearchView().hideSearchView();
});
- drawer.setDrawerHeaderBackground();
+ // drawer.setDrawerHeaderBackground();
}
/**
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java
index c55fb95000..90a98a38a5 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java
@@ -20,7 +20,6 @@
package com.amaze.filemanager.ui.views.drawer;
-import static android.os.Build.VERSION.SDK_INT;
import static com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTPS_URI_PREFIX;
import static com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTP_URI_PREFIX;
import static com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.SSH_URI_PREFIX;
@@ -57,7 +56,6 @@
import com.amaze.filemanager.ui.fragments.CloudSheetFragment;
import com.amaze.filemanager.ui.fragments.FtpServerFragment;
import com.amaze.filemanager.ui.fragments.MainFragment;
-import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants;
import com.amaze.filemanager.ui.fragments.preferencefragments.QuickAccessesPrefsFragment;
import com.amaze.filemanager.ui.theme.AppTheme;
import com.amaze.filemanager.utils.Billing;
@@ -68,8 +66,6 @@
import com.amaze.filemanager.utils.ScreenUtils;
import com.amaze.filemanager.utils.TinyDB;
import com.amaze.filemanager.utils.Utils;
-import com.android.volley.VolleyError;
-import com.android.volley.toolbox.ImageLoader;
import com.cloudrail.si.interfaces.CloudStorage;
import com.cloudrail.si.services.Box;
import com.cloudrail.si.services.Dropbox;
@@ -113,8 +109,6 @@ public class Drawer implements NavigationView.OnNavigationItemSelectedListener {
private static final Logger LOG = LoggerFactory.getLogger(Drawer.class);
- public static final int image_selector_request_code = 31;
-
public static final int STORAGES_GROUP = 0,
SERVERS_GROUP = 1,
CLOUDS_GROUP = 2,
@@ -134,7 +128,6 @@ public class Drawer implements NavigationView.OnNavigationItemSelectedListener {
private boolean isDrawerLocked = false;
private FragmentTransaction pending_fragmentTransaction;
private String pendingPath;
- private ImageLoader mImageLoader;
private String firstPath = null, secondPath = null;
private DrawerLayout mDrawerLayout;
@@ -185,8 +178,6 @@ public Drawer(MainActivity mainActivity) {
return false;
});*/
- mImageLoader = AppConfig.getInstance().getImageLoader();
-
navView = mainActivity.findViewById(R.id.navigation);
// set width of drawer in portrait to follow material guidelines
@@ -742,23 +733,6 @@ private void addNewItem(
}
}
- public void onActivityResult(int requestCode, int responseCode, Intent intent) {
- if (mainActivity.getPrefs() != null && intent != null && intent.getData() != null) {
- if (SDK_INT >= Build.VERSION_CODES.KITKAT) {
- mainActivity
- .getContentResolver()
- .takePersistableUriPermission(intent.getData(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- mainActivity
- .getPrefs()
- .edit()
- .putString(
- PreferencesConstants.PREFERENCE_DRAWER_HEADER_PATH, intent.getData().toString())
- .commit();
- setDrawerHeaderBackground();
- }
- }
-
public void closeIfNotLocked() {
if (!isLocked()) {
close();
@@ -921,32 +895,6 @@ public int getPhoneStorageCount() {
return phoneStorageCount;
}
- public void setDrawerHeaderBackground() {
- String path1 =
- mainActivity.getPrefs().getString(PreferencesConstants.PREFERENCE_DRAWER_HEADER_PATH, null);
- if (path1 == null) {
- return;
- }
- try {
- final ImageView headerImageView = new ImageView(mainActivity);
- headerImageView.setImageDrawable(drawerHeaderParent.getBackground());
- mImageLoader.get(
- path1,
- new ImageLoader.ImageListener() {
- @Override
- public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
- headerImageView.setImageBitmap(response.getBitmap());
- drawerHeaderView.setBackgroundResource(R.drawable.amaze_header_2);
- }
-
- @Override
- public void onErrorResponse(VolleyError error) {}
- });
- } catch (Exception e) {
- LOG.warn("failed to set drawer header background", e);
- }
- }
-
public void selectCorrectDrawerItemForPath(final String path) {
Integer id = dataUtils.findLongestContainingDrawerItem(path);
diff --git a/app/src/main/java/com/amaze/filemanager/utils/LruBitmapCache.java b/app/src/main/java/com/amaze/filemanager/utils/LruBitmapCache.java
deleted file mode 100644
index 66e2103690..0000000000
--- a/app/src/main/java/com/amaze/filemanager/utils/LruBitmapCache.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra ,
- * Emmanuel Messulam, Raymond Lai and Contributors.
- *
- * This file is part of Amaze File Manager.
- *
- * Amaze File Manager is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.amaze.filemanager.utils;
-
-import com.android.volley.toolbox.ImageLoader;
-
-import android.graphics.Bitmap;
-import android.util.LruCache;
-
-/** Created by vishal on 7/6/16. */
-public class LruBitmapCache extends LruCache implements ImageLoader.ImageCache {
- /**
- * @param maxSize for caches that do not override {@link #sizeOf}, this is the maximum number of
- * entries in the cache. For all other caches, this is the maximum sum of the sizes of the
- * entries in this cache.
- */
- public LruBitmapCache(int maxSize) {
- super(maxSize);
- }
-
- public LruBitmapCache() {
- this(getDefaultCacheSize());
- }
-
- private static int getDefaultCacheSize() {
- int memory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- return memory / 8;
- }
-
- @Override
- public Bitmap getBitmap(String url) {
- return get(url);
- }
-
- @Override
- public void putBitmap(String url, Bitmap bitmap) {
- put(url, bitmap);
- }
-
- @Override
- protected int sizeOf(String key, Bitmap value) {
-
- return value.getByteCount();
- }
-}
diff --git a/app/src/test/java/com/amaze/filemanager/application/AppConfigTest.java b/app/src/test/java/com/amaze/filemanager/application/AppConfigTest.java
index b92d24725a..84ea2611b4 100644
--- a/app/src/test/java/com/amaze/filemanager/application/AppConfigTest.java
+++ b/app/src/test/java/com/amaze/filemanager/application/AppConfigTest.java
@@ -90,19 +90,6 @@ public void testToastWithString() {
assertEquals("Hello world", ShadowToast.getTextOfLatestToast());
}
- @Test
- public void testGetImageLoader() throws Exception {
- Field requestQueue = AppConfig.class.getDeclaredField("requestQueue");
- Field imageLoader = AppConfig.class.getDeclaredField("imageLoader");
- requestQueue.setAccessible(true);
- imageLoader.setAccessible(true);
-
- assertNull(requestQueue.get(AppConfig.getInstance()));
- assertNull(imageLoader.get(AppConfig.getInstance()));
-
- assertNotNull(AppConfig.getInstance().getImageLoader());
- }
-
@Test
public void testGlideMemoryCategorySetToHigh() throws Exception {
Field memoryCategory = Glide.class.getDeclaredField("memoryCategory");
From 230313292d6936321985574b337f40791c277744 Mon Sep 17 00:00:00 2001
From: Obolrom
Date: Sat, 1 Jul 2023 20:50:58 +0300
Subject: [PATCH 044/328] ExtractService minor code improvements
---
.../asynchronous/services/ExtractService.java | 44 +++++++++----------
1 file changed, 20 insertions(+), 24 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/services/ExtractService.java b/app/src/main/java/com/amaze/filemanager/asynchronous/services/ExtractService.java
index 48a0d9fff9..74f426a53e 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/services/ExtractService.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/services/ExtractService.java
@@ -24,7 +24,6 @@
import java.io.File;
import java.io.IOException;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import org.apache.commons.compress.PasswordRequiredException;
@@ -59,6 +58,7 @@
import android.widget.RemoteViews;
import android.widget.Toast;
+import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@@ -70,15 +70,14 @@ public class ExtractService extends AbstractProgressiveService {
private final IBinder mBinder = new ObtainableServiceBinder<>(this);
// list of data packages,// to initiate chart in process viewer fragment
- private ArrayList dataPackages = new ArrayList<>();
+ private final ArrayList dataPackages = new ArrayList<>();
private NotificationManagerCompat mNotifyManager;
private NotificationCompat.Builder mBuilder;
- private ProgressHandler progressHandler = new ProgressHandler();
+ private final ProgressHandler progressHandler = new ProgressHandler();
private ProgressListener progressListener;
- private int accentColor;
- private SharedPreferences sharedPreferences;
private RemoteViews customSmallContentViews, customBigContentViews;
+ private @Nullable DoWork extractingAsyncTask;
public static final String KEY_PATH_ZIP = "zip";
public static final String KEY_ENTRIES_ZIP = "entries";
@@ -98,8 +97,9 @@ public int onStartCommand(Intent intent, int flags, final int startId) {
String[] entries = intent.getStringArrayExtra(KEY_ENTRIES_ZIP);
mNotifyManager = NotificationManagerCompat.from(getApplicationContext());
- sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- accentColor =
+ SharedPreferences sharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ int accentColor =
((AppConfig) getApplication())
.getUtilsProvider()
.getColorPreference()
@@ -153,7 +153,8 @@ public int onStartCommand(Intent intent, int flags, final int startId) {
super.onStartCommand(intent, flags, startId);
super.progressHalted();
- new DoWork(this, progressHandler, file, extractPath, entries).execute();
+ extractingAsyncTask = new DoWork(progressHandler, file, extractPath, entries);
+ extractingAsyncTask.execute();
return START_NOT_STICKY;
}
@@ -216,6 +217,9 @@ protected void clearDataPackages() {
@Override
public void onDestroy() {
super.onDestroy();
+ if (extractingAsyncTask != null) {
+ extractingAsyncTask.cancel(true);
+ }
unregisterReceiver(receiver1);
}
@@ -228,21 +232,15 @@ private long getTotalSize(String filePath) {
}
public class DoWork extends AsyncTask {
- private WeakReference extractService;
private String[] entriesToExtract;
- private String extractionPath, compressedPath;
- private ProgressHandler progressHandler;
+ private String extractionPath;
+ private final String compressedPath;
+ private final ProgressHandler progressHandler;
private ServiceWatcherUtil watcherUtil;
private boolean paused = false;
private boolean passwordProtected = false;
- private DoWork(
- ExtractService extractService,
- ProgressHandler progressHandler,
- String cpath,
- String epath,
- String[] entries) {
- this.extractService = new WeakReference<>(extractService);
+ private DoWork(ProgressHandler progressHandler, String cpath, String epath, String[] entries) {
this.progressHandler = progressHandler;
compressedPath = cpath;
extractionPath = epath;
@@ -254,8 +252,7 @@ protected Boolean doInBackground(Void... p) {
while (!isCancelled()) {
if (paused) continue;
- final ExtractService extractService = this.extractService.get();
- if (extractService == null) return null;
+ final ExtractService extractService = ExtractService.this;
File f = new File(compressedPath);
String extractDirName = CompressedHelper.getFileName(f.getName());
@@ -396,7 +393,7 @@ protected void onProgressUpdate(IOException... values) {
(dialog, which) -> {
EditText editText = dialog.getView().findViewById(R.id.singleedittext_input);
ArchivePasswordCache.getInstance().put(compressedPath, editText.getText().toString());
- this.extractService.get().getDataPackages().clear();
+ ExtractService.this.getDataPackages().clear();
this.paused = false;
dialog.dismiss();
},
@@ -413,8 +410,7 @@ protected void onProgressUpdate(IOException... values) {
@Override
public void onPostExecute(Boolean hasInvalidEntries) {
ArchivePasswordCache.getInstance().remove(compressedPath);
- final ExtractService extractService = this.extractService.get();
- if (extractService == null) return;
+ final ExtractService extractService = ExtractService.this;
// check whether watcherutil was initialized. It was not initialized when we got exception
// in extracting the file
@@ -453,7 +449,7 @@ private void toastOnParseError(IOException result) {
* Class used for the client Binder. Because we know this service always runs in the same process
* as its clients, we don't need to deal with IPC.
*/
- private BroadcastReceiver receiver1 =
+ private final BroadcastReceiver receiver1 =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
From 380f89d888a2f023fb326248a02ae75297e8dff4 Mon Sep 17 00:00:00 2001
From: Obolrom
Date: Sat, 1 Jul 2023 20:59:22 +0300
Subject: [PATCH 045/328] CopyService minor code improvements
---
.../asynchronous/services/CopyService.java | 29 +++++++------------
1 file changed, 11 insertions(+), 18 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java b/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java
index e83fdd4af7..fbb0ae012a 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/services/CopyService.java
@@ -85,18 +85,12 @@ public class CopyService extends AbstractProgressiveService {
private final IBinder mBinder = new ObtainableServiceBinder<>(this);
private ServiceWatcherUtil watcherUtil;
- private ProgressHandler progressHandler = new ProgressHandler();
+ private final ProgressHandler progressHandler = new ProgressHandler();
private ProgressListener progressListener;
// list of data packages, to initiate chart in process viewer fragment
- private ArrayList dataPackages = new ArrayList<>();
- private int accentColor;
- private SharedPreferences sharedPreferences;
+ private final ArrayList dataPackages = new ArrayList<>();
private RemoteViews customSmallContentViews, customBigContentViews;
- private boolean isRootExplorer;
- private long totalSize = 0L;
- private int totalSourceFiles = 0;
-
@Override
public void onCreate() {
super.onCreate();
@@ -107,13 +101,13 @@ public void onCreate() {
@Override
public int onStartCommand(Intent intent, int flags, final int startId) {
Bundle b = new Bundle();
- isRootExplorer = intent.getBooleanExtra(TAG_IS_ROOT_EXPLORER, false);
+ boolean isRootExplorer = intent.getBooleanExtra(TAG_IS_ROOT_EXPLORER, false);
ArrayList files = intent.getParcelableArrayListExtra(TAG_COPY_SOURCES);
String targetPath = intent.getStringExtra(TAG_COPY_TARGET);
int mode = intent.getIntExtra(TAG_COPY_OPEN_MODE, OpenMode.UNKNOWN.ordinal());
final boolean move = intent.getBooleanExtra(TAG_COPY_MOVE, false);
- sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c);
- accentColor =
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c);
+ int accentColor =
((AppConfig) getApplication())
.getUtilsProvider()
.getColorPreference()
@@ -238,7 +232,7 @@ private class DoInBackground extends AsyncTask {
boolean move;
private Copy copy;
private String targetPath;
- private boolean isRootExplorer;
+ private final boolean isRootExplorer;
private int sourceProgress = 0;
private DoInBackground(boolean isRootExplorer) {
@@ -251,8 +245,8 @@ protected Void doInBackground(Bundle... p1) {
// setting up service watchers and initial data packages
// finding total size on background thread (this is necessary condition for SMB!)
- totalSize = FileUtils.getTotalBytes(sourceFiles, c);
- totalSourceFiles = sourceFiles.size();
+ long totalSize = FileUtils.getTotalBytes(sourceFiles, c);
+ int totalSourceFiles = sourceFiles.size();
progressHandler.setSourceSize(totalSourceFiles);
progressHandler.setTotalSize(totalSize);
@@ -435,7 +429,7 @@ public void execute(
}
}
} else {
- for (HybridFileParcelable f : sourceFiles) failedFOps.add(f);
+ failedFOps.addAll(sourceFiles);
return;
}
@@ -455,7 +449,7 @@ void copyRoot(HybridFileParcelable sourceFile, HybridFile targetFile, boolean mo
try {
if (!move) {
CopyFilesCommand.INSTANCE.copyFiles(sourceFile.getPath(), targetFile.getPath());
- } else if (move) {
+ } else {
MoveFileCommand.INSTANCE.moveFile(sourceFile.getPath(), targetFile.getPath());
}
ServiceWatcherUtil.position += sourceFile.getSize();
@@ -532,7 +526,7 @@ private void copyFiles(
}
}
- private BroadcastReceiver receiver3 =
+ private final BroadcastReceiver receiver3 =
new BroadcastReceiver() {
@Override
@@ -544,7 +538,6 @@ public void onReceive(Context context, Intent intent) {
@Override
public IBinder onBind(Intent arg0) {
- // TODO Auto-generated method stub
return mBinder;
}
}
From e7cf56aad41f3dabf9b571503f59e3381cf84919 Mon Sep 17 00:00:00 2001
From: Obolrom
Date: Sat, 1 Jul 2023 21:01:25 +0300
Subject: [PATCH 046/328] DecryptService minor code improvements
---
.../filemanager/asynchronous/services/DecryptService.java | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/services/DecryptService.java b/app/src/main/java/com/amaze/filemanager/asynchronous/services/DecryptService.java
index c05d326b7b..e392a607e9 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/services/DecryptService.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/services/DecryptService.java
@@ -34,7 +34,6 @@
import com.amaze.filemanager.asynchronous.asynctasks.Task;
import com.amaze.filemanager.asynchronous.asynctasks.TaskKt;
import com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil;
-import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.filesystem.FileProperties;
import com.amaze.filemanager.filesystem.HybridFile;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
@@ -113,8 +112,6 @@ public int onStartCommand(Intent intent, int flags, int startId) {
.getCurrentUserColorPreferences(this, sharedPreferences)
.getAccent();
- OpenMode openMode =
- OpenMode.values()[intent.getIntExtra(TAG_OPEN_MODE, OpenMode.UNKNOWN.ordinal())];
notificationManager = NotificationManagerCompat.from(getApplicationContext());
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
@@ -267,7 +264,7 @@ public Callable getTask() {
// and the cache directory in case we're here because of the viewer
try {
new CryptUtil(context, baseFile, decryptPath, progressHandler, failedOps, password);
- } catch (AESCrypt.DecryptFailureException e) {
+ } catch (AESCrypt.DecryptFailureException ignored) {
} catch (Exception e) {
LOG.error("Error decrypting " + baseFile.getPath(), e);
@@ -295,7 +292,7 @@ public void onDestroy() {
this.unregisterReceiver(cancelReceiver);
}
- private BroadcastReceiver cancelReceiver =
+ private final BroadcastReceiver cancelReceiver =
new BroadcastReceiver() {
@Override
From 846684444b0723ad168382dbfd723166590c9443 Mon Sep 17 00:00:00 2001
From: Obolrom
Date: Sat, 1 Jul 2023 21:02:43 +0300
Subject: [PATCH 047/328] EncryptService minor code improvements
---
.../asynchronous/services/EncryptService.java | 21 +++++++------------
1 file changed, 8 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/asynchronous/services/EncryptService.java b/app/src/main/java/com/amaze/filemanager/asynchronous/services/EncryptService.java
index 897b64a109..28e1a1e128 100644
--- a/app/src/main/java/com/amaze/filemanager/asynchronous/services/EncryptService.java
+++ b/app/src/main/java/com/amaze/filemanager/asynchronous/services/EncryptService.java
@@ -33,7 +33,6 @@
import com.amaze.filemanager.asynchronous.asynctasks.Task;
import com.amaze.filemanager.asynchronous.asynctasks.TaskKt;
import com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil;
-import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.filesystem.FileProperties;
import com.amaze.filemanager.filesystem.HybridFile;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
@@ -77,20 +76,18 @@ public class EncryptService extends AbstractProgressiveService {
private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder notificationBuilder;
private Context context;
- private IBinder mBinder = new ObtainableServiceBinder<>(this);
- private ProgressHandler progressHandler = new ProgressHandler();
+ private final IBinder binder = new ObtainableServiceBinder<>(this);
+ private final ProgressHandler progressHandler = new ProgressHandler();
private ProgressListener progressListener;
// list of data packages, to initiate chart in process viewer fragment
- private ArrayList dataPackages = new ArrayList<>();
+ private final ArrayList dataPackages = new ArrayList<>();
private ServiceWatcherUtil serviceWatcherUtil;
private long totalSize = 0L;
private HybridFileParcelable baseFile;
- private ArrayList failedOps = new ArrayList<>();
+ private final ArrayList failedOps = new ArrayList<>();
private String targetFilename;
- private int accentColor;
private boolean useAesCrypt;
private String password;
- private SharedPreferences sharedPreferences;
private RemoteViews customSmallContentViews, customBigContentViews;
@Override
@@ -110,16 +107,14 @@ public int onStartCommand(Intent intent, int flags, int startId) {
if (useAesCrypt) {
password = intent.getStringExtra(TAG_PASSWORD);
}
- sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
- accentColor =
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ int accentColor =
((AppConfig) getApplication())
.getUtilsProvider()
.getColorPreference()
.getCurrentUserColorPreferences(this, sharedPreferences)
.getAccent();
- OpenMode openMode =
- OpenMode.values()[intent.getIntExtra(TAG_OPEN_MODE, OpenMode.UNKNOWN.ordinal())];
notificationManager = NotificationManagerCompat.from(getApplicationContext());
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
@@ -284,7 +279,7 @@ public Callable getTask() {
@Override
public IBinder onBind(Intent intent) {
- return mBinder;
+ return binder;
}
@Override
@@ -293,7 +288,7 @@ public void onDestroy() {
this.unregisterReceiver(cancelReceiver);
}
- private BroadcastReceiver cancelReceiver =
+ private final BroadcastReceiver cancelReceiver =
new BroadcastReceiver() {
@Override
From 28e5b73fea9983dd2e2982d0d7adc587be6e4820 Mon Sep 17 00:00:00 2001
From: Vishal Nehra
Date: Fri, 7 Jul 2023 12:07:52 +0530
Subject: [PATCH 048/328] Minor changes in smb search dialog
---
.../java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
index 014f5b5d3b..0b07aae815 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/SmbSearchDialog.kt
@@ -96,7 +96,7 @@ class SmbSearchDialog : DialogFragment() {
}
builder.positiveText(R.string.use_custom_ip)
builder.positiveColor(accentColor)
- viewModel.valHolder.value = (ComputerParcelable("-1", "-1"))
+ viewModel.valHolder.postValue(ComputerParcelable("-1", "-1"))
listViewAdapter = ListViewAdapter(requireActivity())
val observable = SmbDeviceScannerObservable()
subnetScannerObserver = observable
@@ -108,7 +108,7 @@ class SmbSearchDialog : DialogFragment() {
.subscribe(
{ computer: ComputerParcelable ->
if (!listViewAdapter.contains(computer)) {
- viewModel.valHolder.value = computer
+ viewModel.valHolder.postValue(computer)
}
},
{ err: Throwable ->
@@ -156,7 +156,6 @@ class SmbSearchDialog : DialogFragment() {
items.add(computer)
} else {
items.add(items.size - 1, computer)
- removeDummy()
}
notifyDataSetChanged()
}
From 83f618411de094eae633cab1f01ee35b8b2629c5 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sun, 9 Jul 2023 12:33:51 +0800
Subject: [PATCH 049/328] Migrate removed Volley dependency to OkHttp
To fix missing Volley dependency caused by #3872 when #3704 was still depends on Volley
---
app/build.gradle | 3 +
.../utils/smb/WsddDiscoverDeviceStrategy.kt | 61 ++++++++-----------
build.gradle | 1 +
.../filemanager/test/volley/MockHttpStack.kt | 51 ----------------
4 files changed, 31 insertions(+), 85 deletions(-)
delete mode 100644 testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt
diff --git a/app/build.gradle b/app/build.gradle
index eeafa75fb6..6d4e53e4a4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -204,6 +204,9 @@ dependencies {
//FTP
implementation "commons-net:commons-net:$commonsNetVersion"
+ //OkHttp
+ implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
+
implementation "org.bouncycastle:bcpkix-jdk15on:$bouncyCastleVersion"
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
diff --git a/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt b/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt
index 33e1813c69..db5a1ab86f 100644
--- a/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt
+++ b/app/src/main/java/com/amaze/filemanager/utils/smb/WsddDiscoverDeviceStrategy.kt
@@ -23,12 +23,14 @@ package com.amaze.filemanager.utils.smb
import androidx.annotation.VisibleForTesting
import com.amaze.filemanager.R
import com.amaze.filemanager.application.AppConfig
+import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.SLASH
import com.amaze.filemanager.utils.ComputerParcelable
import com.amaze.filemanager.utils.NetworkUtil
-import com.android.volley.Response.ErrorListener
-import com.android.volley.VolleyError
-import com.android.volley.toolbox.StringRequest
-import com.android.volley.toolbox.Volley
+import okhttp3.Headers.Companion.toHeaders
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.xmlpull.v1.XmlPullParser
@@ -75,7 +77,6 @@ class WsddDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStra
private val wsdRequestHeaders = mutableMapOf(
Pair("Accept-Encoding", "Identity"),
- Pair("Content-Type", "application/soap+xml"),
Pair("Connection", "Close"),
Pair("User-Agent", "wsd")
)
@@ -87,14 +88,10 @@ class WsddDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStra
@VisibleForTesting
set
- private val queue = Volley.newRequestQueue(AppConfig.getInstance())
+ private val queue = OkHttpClient()
private var cancelled = false
- init {
- queue.start()
- }
-
override fun discoverDevices(callback: (ComputerParcelable) -> Unit) {
multicastForDevice { addr ->
callback.invoke(addr)
@@ -181,21 +178,30 @@ class WsddDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStra
val endpoint = urn.substringAfter(URN_UUID)
val dest =
"http://${sourceAddress.hostAddress}:$TCP_PORT/$endpoint"
- queue.add(
- object : StringRequest(
- Method.POST,
- dest,
- { resp ->
+ val requestBody = wsdRequestTemplate
+ .replace("##MESSAGE_ID##", "$URN_UUID$messageId")
+ .replace("##DEST_UUID##", urn)
+ .replace("##MY_UUID##", "$URN_UUID$tempDeviceId")
+ .toRequestBody("application/soap+xml".toMediaType())
+ queue.newCall(
+ Request.Builder()
+ .url(dest)
+ .post(requestBody)
+ .headers(wsdRequestHeaders.toHeaders())
+ .build()
+ ).execute().use { resp ->
+ if (resp.isSuccessful && resp.body != null) {
+ resp.body?.run {
if (log.isTraceEnabled) log.trace("Response: $resp")
val values = parseXmlForResponse(
- resp,
+ this.string(),
arrayOf(WSDP_TYPES, WSA_ADDRESS, PUB_COMPUTER)
)
if (PUB_COMPUTER == values[WSDP_TYPES] && urn == values[WSA_ADDRESS]) {
if (true == values[PUB_COMPUTER]?.isNotEmpty()) {
val computerName: String = values[PUB_COMPUTER].let {
- if (it!!.contains('/')) {
- it.substringBefore("/")
+ if (it!!.contains(SLASH)) {
+ it.substringBefore(SLASH)
} else {
it
}
@@ -205,29 +211,16 @@ class WsddDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStra
)
}
}
- },
- object : ErrorListener {
- override fun onErrorResponse(error: VolleyError?) {
- log.error("Error querying endpoint", error)
- }
}
- ) {
- override fun getBody(): ByteArray {
- return wsdRequestTemplate
- .replace("##MESSAGE_ID##", "$URN_UUID$messageId")
- .replace("##DEST_UUID##", urn)
- .replace("##MY_UUID##", "$URN_UUID$tempDeviceId")
- .toByteArray(Charsets.UTF_8)
- }
- override fun getHeaders(): MutableMap = wsdRequestHeaders
+ } else {
+ log.error("Error querying endpoint", resp)
}
- )
+ }
}
}
override fun onCancel() {
cancelled = true
- queue.stop()
}
private fun parseXmlForResponse(xml: ByteArray, tags: Array) =
diff --git a/build.gradle b/build.gradle
index 3fd0ef3566..3d018b2808 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,6 +39,7 @@ buildscript {
jsoupVersion = "1.13.1"
rxAndroidVersion = "2.1.1"
rxJavaVersion = "2.2.9"
+ okHttpVersion = "4.9.0"
}
repositories {
google()
diff --git a/testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt b/testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt
deleted file mode 100644
index f859a35b43..0000000000
--- a/testShared/src/test/java/com/amaze/filemanager/test/volley/MockHttpStack.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.amaze.filemanager.test.volley
-
-import com.android.volley.AuthFailureError
-import com.android.volley.Request
-import com.android.volley.toolbox.BaseHttpStack
-import com.android.volley.toolbox.HttpResponse
-import java.io.IOException
-
-/**
- * Mock [BaseHttpStack] for test only.
- */
-class MockHttpStack : BaseHttpStack() {
-
- private lateinit var mResponseToReturn: HttpResponse
- private lateinit var lastUrl: String
- private lateinit var mLastHeaders: MutableMap
- private var lastPostBody: ByteArray? = null
-
- /**
- * get headers in last request
- */
- fun getLastHeaders() = mLastHeaders
-
- /**
- * Manually set response to return
- */
- fun setResponseToReturn(response: HttpResponse) {
- mResponseToReturn = response
- }
-
- @Throws(IOException::class, AuthFailureError::class)
- override fun executeRequest(
- request: Request<*>,
- additionalHeaders: Map?
- ): HttpResponse {
- lastUrl = request.url
- mLastHeaders = HashMap()
- if (request.headers != null) {
- mLastHeaders.putAll(request.headers)
- }
- if (additionalHeaders != null) {
- mLastHeaders.putAll(additionalHeaders)
- }
- try {
- lastPostBody = request.body
- } catch (e: AuthFailureError) {
- lastPostBody = null
- }
- return mResponseToReturn
- }
-}
From 356068d69786630313083bfc9ec8f10f95680843 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sat, 18 Feb 2023 11:40:45 +0800
Subject: [PATCH 050/328] SDK 33 adaptations + lib upgrades
It has to happen anyway.
- Compile with SDK 33
- Use Kotlin 1.8
- Use Java 11
- Library upgrades
- Fix syntax and API problems brought by the upgrade
---
app/build.gradle | 19 +++++++++---------
.../ui/base/BaseBottomSheetFragment.kt | 4 ++--
.../ui/views/drawer/HasherOfMenuItem.kt | 2 +-
.../database/MigrationTestHelper.java | 14 ++++++-------
build.gradle | 20 +++++++++----------
commons_compress_7z/build.gradle | 2 +-
file_operations/build.gradle | 4 ++--
7 files changed, 32 insertions(+), 33 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index eeafa75fb6..204cd68677 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ apply plugin: 'com.hiya.jacoco-android'
apply plugin: "com.starter.easylauncher"
android {
- compileSdkVersion 31
+ compileSdkVersion 33
packagingOptions {
resources {
excludes += ['proguard-project.txt', 'project.properties', 'META-INF/LICENSE.txt', 'META-INF/LICENSE', 'META-INF/NOTICE.txt', 'META-INF/NOTICE', 'META-INF/DEPENDENCIES.txt', 'META-INF/DEPENDENCIES']
@@ -17,7 +17,7 @@ android {
defaultConfig {
applicationId "com.amaze.filemanager"
minSdkVersion 19
- targetSdkVersion 31
+ targetSdkVersion 33
versionCode 118
versionName "3.8.5"
multiDexEnabled true
@@ -85,13 +85,12 @@ android {
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
- useIR = true
+ jvmTarget = '11'
}
testOptions {
@@ -252,11 +251,11 @@ dependencies {
configurations.all {
resolutionStrategy {
dependencySubstitution {
- substitute module("commons-logging:commons-logging-api:1.1") with module("commons-logging:commons-logging:1.1.1")
- substitute module("com.android.support:support-annotations:27.1.1") with module("com.android.support:support-annotations:27.0.2")
+ substitute module("commons-logging:commons-logging-api:1.1") using module("commons-logging:commons-logging:1.1.1")
+ substitute module("com.android.support:support-annotations:27.1.1") using module("com.android.support:support-annotations:27.0.2")
// These two lines are added to prevent possible class clashes between awaitility (which uses hamcrest 2.1) and junit (which uses hamcrest 1.3).
- substitute module('org.hamcrest:hamcrest-core:1.3') with module("org.hamcrest:hamcrest:2.1")
- substitute module('org.hamcrest:hamcrest-library:1.3') with module("org.hamcrest:hamcrest:2.1")
+ substitute module('org.hamcrest:hamcrest-core:1.3') using module("org.hamcrest:hamcrest:2.1")
+ substitute module('org.hamcrest:hamcrest-library:1.3') using module("org.hamcrest:hamcrest:2.1")
}
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt
index 78c3b6449f..c8e6551fa8 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/base/BaseBottomSheetFragment.kt
@@ -39,7 +39,7 @@ open class BaseBottomSheetFragment : BottomSheetDialogFragment() {
* Initializes bottom sheet ui resources based on current theme
*/
fun initDialogResources(rootView: View) {
- when ((activity as ThemedActivity?)!!.appTheme!!) {
+ when ((requireActivity() as ThemedActivity).appTheme!!) {
AppTheme.DARK -> {
rootView.setBackgroundDrawable(
context?.resources?.getDrawable(
@@ -54,7 +54,7 @@ open class BaseBottomSheetFragment : BottomSheetDialogFragment() {
)
)
}
- AppTheme.LIGHT, AppTheme.TIMED -> {
+ AppTheme.LIGHT, AppTheme.TIMED, AppTheme.SYSTEM -> {
rootView
.setBackgroundDrawable(
context?.resources?.getDrawable(
diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt
index 1e09c91473..bbf04494d1 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt
+++ b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/HasherOfMenuItem.kt
@@ -28,7 +28,7 @@ import android.view.MenuItem
data class HasherOfMenuItem(
val groupId: Int,
val itemId: Int,
- val title: CharSequence,
+ val title: CharSequence?,
val ordering: Int
)
diff --git a/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java b/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java
index d5dcacc0c7..e5094cfa30 100644
--- a/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java
+++ b/app/src/test/java/com/amaze/filemanager/database/MigrationTestHelper.java
@@ -477,7 +477,7 @@ static class MigratingDelegate extends MigrationTestHelper.RoomOpenHelperDelegat
}
@Override
- protected void createAllTables(SupportSQLiteDatabase database) {
+ public void createAllTables(SupportSQLiteDatabase database) {
throw new UnsupportedOperationException(
"Was expecting to migrate but received create."
+ "Make sure you have created the database first.");
@@ -485,7 +485,7 @@ protected void createAllTables(SupportSQLiteDatabase database) {
@NonNull
@Override
- protected RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
+ public RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
final Map tables = mDatabaseBundle.getEntitiesByTableName();
for (EntityBundle entity : tables.values()) {
if (entity instanceof FtsEntityBundle) {
@@ -548,7 +548,7 @@ static class CreatingDelegate extends MigrationTestHelper.RoomOpenHelperDelegate
}
@Override
- protected void createAllTables(SupportSQLiteDatabase database) {
+ public void createAllTables(SupportSQLiteDatabase database) {
for (String query : mDatabaseBundle.buildCreateQueries()) {
database.execSQL(query);
}
@@ -556,7 +556,7 @@ protected void createAllTables(SupportSQLiteDatabase database) {
@NonNull
@Override
- protected RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
+ public RoomOpenHelper.ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
throw new UnsupportedOperationException(
"This open helper just creates the database but" + " it received a migration request.");
}
@@ -571,14 +571,14 @@ abstract static class RoomOpenHelperDelegate extends RoomOpenHelper.Delegate {
}
@Override
- protected void dropAllTables(SupportSQLiteDatabase database) {
+ public void dropAllTables(SupportSQLiteDatabase database) {
throw new UnsupportedOperationException("cannot drop all tables in the test");
}
@Override
- protected void onCreate(SupportSQLiteDatabase database) {}
+ public void onCreate(SupportSQLiteDatabase database) {}
@Override
- protected void onOpen(SupportSQLiteDatabase database) {}
+ public void onOpen(SupportSQLiteDatabase database) {}
}
}
diff --git a/build.gradle b/build.gradle
index 3fd0ef3566..1d9736c176 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,23 +2,23 @@
buildscript {
ext {
- kotlin_version = "1.6.10"
+ kotlin_version = "1.8.0"
robolectricVersion = '4.9'
glideVersion = '4.11.0'
sshjVersion = '0.34.0'
jcifsVersion = '2.1.6'
fabSpeedDialVersion = '3.1.1'
- roomVersion = '2.4.3'
+ roomVersion = '2.5.0'
bouncyCastleVersion = '1.70'
awaitilityVersion = "3.1.6"
androidXCoreVersion = "1.7.0"
- androidMaterialVersion = "1.4.0" // Upgrade to 1.5 requires targetSdkVersion 31
- androidXFragmentVersion = "1.4.1"
- androidXAppCompatVersion = "1.4.1"
- androidXAnnotationVersion = "1.3.0"
+ androidMaterialVersion = "1.5.0" // Upgrade to 1.5 requires targetSdkVersion 31
+ androidXFragmentVersion = "1.5.5"
+ androidXAppCompatVersion = "1.6.1"
+ androidXAnnotationVersion = "1.5.0"
androidXPrefVersion = "1.2.0"
- androidXTestVersion = "1.4.0"
- androidXTestExtVersion = "1.1.3"
+ androidXTestVersion = "1.5.0"
+ androidXTestExtVersion = "1.1.5"
uiAutomatorVersion = "2.2.0"
junitVersion = "4.13.2"
slf4jVersion = "1.7.25"
@@ -27,7 +27,7 @@ buildscript {
androidBillingVersion = "5.0.0"
junrarVersion = "7.4.0"
zip4jVersion = "2.6.4"
- espressoVersion = "3.4.0"
+ espressoVersion = "3.5.1"
materialDialogsVersion = "0.9.6.0"
jacocoVersion = "0.8.7"
commonsCompressVersion = "1.22"
@@ -68,7 +68,7 @@ allprojects {
maven { url "https://jcenter.bintray.com" }
}
tasks.withType(Test) {
- maxParallelForks = 4
+ maxParallelForks = 8
maxHeapSize = "2g"
forkEvery = 4
failFast = true
diff --git a/commons_compress_7z/build.gradle b/commons_compress_7z/build.gradle
index 0b7e3a9e9a..428e80db73 100644
--- a/commons_compress_7z/build.gradle
+++ b/commons_compress_7z/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 28
+ compileSdkVersion 33
defaultConfig {
minSdkVersion 19
diff --git a/file_operations/build.gradle b/file_operations/build.gradle
index eadddd661e..47cefa5e63 100644
--- a/file_operations/build.gradle
+++ b/file_operations/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 30
+ compileSdkVersion 33
defaultConfig {
minSdkVersion 19
@@ -51,7 +51,7 @@ android {
}
kotlinOptions {
- useIR = true
+ jvmTarget = '11'
}
}
From 43d148f5380bb6c2d783757bd466932e36265159 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Thu, 20 Jul 2023 00:17:07 +0800
Subject: [PATCH 051/328] Changes per feedback
Add POST_NOTIFICATION permission request
---
app/src/main/AndroidManifest.xml | 1 +
.../ui/activities/MainActivity.java | 7 +++-
.../superclasses/PermissionsActivity.java | 38 +++++++++++++++++--
app/src/main/res/values/strings.xml | 2 +
4 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 96ff20b8a3..5bd6da5ace 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -39,6 +39,7 @@
+
= Build.VERSION_CODES.M) {
+ if (SDK_INT >= Build.VERSION_CODES.M) {
if (!checkStoragePermission()) {
requestStoragePermission(this, true);
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (SDK_INT >= Build.VERSION_CODES.R) {
requestAllFilesAccess(this);
}
+ if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestNotificationPermission(true);
+ }
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
index 9c97929871..c0bd3a31a9 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
@@ -20,6 +20,8 @@
package com.amaze.filemanager.ui.activities.superclasses;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.amaze.filemanager.R;
@@ -48,10 +50,11 @@ public class PermissionsActivity extends ThemedActivity
private static final String TAG = PermissionsActivity.class.getSimpleName();
- public static final int PERMISSION_LENGTH = 3;
+ public static final int PERMISSION_LENGTH = 4;
public static final int STORAGE_PERMISSION = 0,
INSTALL_APK_PERMISSION = 1,
- ALL_FILES_PERMISSION = 2;
+ ALL_FILES_PERMISSION = 2,
+ NOTIFICATION_PERMISSION = 3;
private final OnPermissionGranted[] permissionCallbacks =
new OnPermissionGranted[PERMISSION_LENGTH];
@@ -69,7 +72,13 @@ public void onRequestPermissionsResult(
Toast.makeText(this, R.string.grantfailed, Toast.LENGTH_SHORT).show();
requestStoragePermission(permissionCallbacks[STORAGE_PERMISSION], false);
}
-
+ } else if (requestCode == NOTIFICATION_PERMISSION && Build.VERSION.SDK_INT >= TIRAMISU) {
+ if (isGranted(grantResults)) {
+ Utils.enableScreenRotation(this);
+ } else {
+ Toast.makeText(this, R.string.grantfailed, Toast.LENGTH_SHORT).show();
+ requestNotificationPermission(false);
+ }
} else if (requestCode == INSTALL_APK_PERMISSION) {
if (isGranted(grantResults)) {
permissionCallbacks[INSTALL_APK_PERMISSION].onPermissionGranted();
@@ -84,6 +93,29 @@ public boolean checkStoragePermission() {
== PackageManager.PERMISSION_GRANTED;
}
+ @RequiresApi(api = TIRAMISU)
+ public void requestNotificationPermission(boolean isInitialStart) {
+ Utils.disableScreenRotation(this);
+ final MaterialDialog materialDialog =
+ GeneralDialogCreation.showBasicDialog(
+ this,
+ R.string.grant_notification_permission,
+ R.string.grantper,
+ R.string.grant,
+ R.string.cancel);
+ materialDialog.getActionButton(DialogAction.NEGATIVE).setOnClickListener(v -> finish());
+ materialDialog.setCancelable(false);
+
+ requestPermission(
+ Manifest.permission.POST_NOTIFICATIONS,
+ NOTIFICATION_PERMISSION,
+ materialDialog,
+ () -> {
+ //do nothing
+ },
+ isInitialStart);
+ }
+
public void requestStoragePermission(
@NonNull final OnPermissionGranted onPermissionGranted, boolean isInitialStart) {
Utils.disableScreenRotation(this);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8fc0ca66fc..8bec20b847 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -813,5 +813,7 @@ You only need to do this once, until the next time you select a new location for
Try Indexed Search!
Recent
Results
+
+ Amaze needs notification permission to display file operation progress, as well as a handy panel to start/stop the FTP server.
From b71b711551234ffae824a8d11af9cfb488e53913 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Thu, 20 Jul 2023 00:17:07 +0800
Subject: [PATCH 052/328] Changes per feedback
- Add POST_NOTIFICATION permission request
- Add additional permissions required by API 33 for storage access
---
.../ftp/FtpServiceStaticMethodsTest.kt | 5 ++-
.../security/SecretKeygenEspressoTest.kt | 2 +
app/src/main/AndroidManifest.xml | 5 +++
.../ui/activities/MainActivity.java | 7 ++-
.../superclasses/PermissionsActivity.java | 44 +++++++++++++++++--
app/src/main/res/values/strings.xml | 2 +
6 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/app/src/androidTest/java/com/amaze/filemanager/asynchronous/services/ftp/FtpServiceStaticMethodsTest.kt b/app/src/androidTest/java/com/amaze/filemanager/asynchronous/services/ftp/FtpServiceStaticMethodsTest.kt
index 830e55fe91..afa89840a9 100644
--- a/app/src/androidTest/java/com/amaze/filemanager/asynchronous/services/ftp/FtpServiceStaticMethodsTest.kt
+++ b/app/src/androidTest/java/com/amaze/filemanager/asynchronous/services/ftp/FtpServiceStaticMethodsTest.kt
@@ -26,6 +26,7 @@ import android.os.Build.VERSION_CODES.N_MR1
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.amaze.filemanager.utils.NetworkUtil
import org.junit.Assert.assertNotNull
import org.junit.Assert.fail
import org.junit.Test
@@ -53,11 +54,11 @@ class FtpServiceStaticMethodsTest {
*/
if (SDK_INT >= N_MR1) {
ApplicationProvider.getApplicationContext().run {
- if (!FtpService.isConnectedToLocalNetwork(this)) {
+ if (!NetworkUtil.isConnectedToLocalNetwork(this)) {
fail("Please connect your device to network to run this test!")
}
- FtpService.getLocalInetAddress(this).also {
+ NetworkUtil.getLocalInetAddress(this).also {
assertNotNull(it)
assertNotNull(it?.hostAddress)
}
diff --git a/app/src/androidTest/java/com/amaze/filemanager/utils/security/SecretKeygenEspressoTest.kt b/app/src/androidTest/java/com/amaze/filemanager/utils/security/SecretKeygenEspressoTest.kt
index 773113c169..293accbf3e 100644
--- a/app/src/androidTest/java/com/amaze/filemanager/utils/security/SecretKeygenEspressoTest.kt
+++ b/app/src/androidTest/java/com/amaze/filemanager/utils/security/SecretKeygenEspressoTest.kt
@@ -51,6 +51,8 @@ class SecretKeygenEspressoTest {
assertEquals("aes", this.algorithm.lowercase())
} ?: if (SDK_INT < ICE_CREAM_SANDWICH) {
fail("Android version not supported")
+ } else {
+ // do nothing
}
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 96ff20b8a3..92c271bee6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,7 @@
+
@@ -39,6 +40,10 @@
+
+
+
+
= Build.VERSION_CODES.M) {
+ if (SDK_INT >= Build.VERSION_CODES.M) {
if (!checkStoragePermission()) {
requestStoragePermission(this, true);
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (SDK_INT >= Build.VERSION_CODES.R) {
requestAllFilesAccess(this);
}
+ if (SDK_INT >= Build.VERSION_CODES.TIRAMISU && !checkNotificationPermission()) {
+ requestNotificationPermission(true);
+ }
}
}
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
index 9c97929871..7204cee67c 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
@@ -20,6 +20,8 @@
package com.amaze.filemanager.ui.activities.superclasses;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.amaze.filemanager.R;
@@ -48,10 +50,11 @@ public class PermissionsActivity extends ThemedActivity
private static final String TAG = PermissionsActivity.class.getSimpleName();
- public static final int PERMISSION_LENGTH = 3;
+ public static final int PERMISSION_LENGTH = 4;
public static final int STORAGE_PERMISSION = 0,
INSTALL_APK_PERMISSION = 1,
- ALL_FILES_PERMISSION = 2;
+ ALL_FILES_PERMISSION = 2,
+ NOTIFICATION_PERMISSION = 3;
private final OnPermissionGranted[] permissionCallbacks =
new OnPermissionGranted[PERMISSION_LENGTH];
@@ -69,7 +72,13 @@ public void onRequestPermissionsResult(
Toast.makeText(this, R.string.grantfailed, Toast.LENGTH_SHORT).show();
requestStoragePermission(permissionCallbacks[STORAGE_PERMISSION], false);
}
-
+ } else if (requestCode == NOTIFICATION_PERMISSION && Build.VERSION.SDK_INT >= TIRAMISU) {
+ if (isGranted(grantResults)) {
+ Utils.enableScreenRotation(this);
+ } else {
+ Toast.makeText(this, R.string.grantfailed, Toast.LENGTH_SHORT).show();
+ requestNotificationPermission(false);
+ }
} else if (requestCode == INSTALL_APK_PERMISSION) {
if (isGranted(grantResults)) {
permissionCallbacks[INSTALL_APK_PERMISSION].onPermissionGranted();
@@ -84,6 +93,35 @@ public boolean checkStoragePermission() {
== PackageManager.PERMISSION_GRANTED;
}
+ @RequiresApi(TIRAMISU)
+ public boolean checkNotificationPermission() {
+ return ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @RequiresApi(TIRAMISU)
+ public void requestNotificationPermission(boolean isInitialStart) {
+ Utils.disableScreenRotation(this);
+ final MaterialDialog materialDialog =
+ GeneralDialogCreation.showBasicDialog(
+ this,
+ R.string.grant_notification_permission,
+ R.string.grantper,
+ R.string.grant,
+ R.string.cancel);
+ materialDialog.getActionButton(DialogAction.NEGATIVE).setOnClickListener(v -> finish());
+ materialDialog.setCancelable(false);
+
+ requestPermission(
+ Manifest.permission.POST_NOTIFICATIONS,
+ NOTIFICATION_PERMISSION,
+ materialDialog,
+ () -> {
+ // do nothing
+ },
+ isInitialStart);
+ }
+
public void requestStoragePermission(
@NonNull final OnPermissionGranted onPermissionGranted, boolean isInitialStart) {
Utils.disableScreenRotation(this);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8fc0ca66fc..8bec20b847 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -813,5 +813,7 @@ You only need to do this once, until the next time you select a new location for
Try Indexed Search!
Recent
Results
+
+ Amaze needs notification permission to display file operation progress, as well as a handy panel to start/stop the FTP server.
From 09fdaf3c3ee31bb931f5dd70100c1e56ae9289c3 Mon Sep 17 00:00:00 2001
From: Vishal Nehra
Date: Fri, 21 Jul 2023 03:51:42 +0530
Subject: [PATCH 053/328] Modifications in asking storage permission in T
---
.../ui/activities/MainActivity.java | 9 ++-
.../superclasses/PermissionsActivity.java | 73 ++++++++++++-------
2 files changed, 53 insertions(+), 29 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java
index 455e88e98a..23c2eeb3e3 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java
@@ -557,10 +557,11 @@ public void onPermissionGranted() {
private void checkForExternalPermission() {
if (SDK_INT >= Build.VERSION_CODES.M) {
if (!checkStoragePermission()) {
- requestStoragePermission(this, true);
- }
- if (SDK_INT >= Build.VERSION_CODES.R) {
- requestAllFilesAccess(this);
+ if (SDK_INT >= Build.VERSION_CODES.R) {
+ requestAllFilesAccess(this);
+ } else {
+ requestStoragePermission(this, true);
+ }
}
if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestNotificationPermission(true);
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
index c0bd3a31a9..d8b374d3a9 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
@@ -20,12 +20,14 @@
package com.amaze.filemanager.ui.activities.superclasses;
+import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.amaze.filemanager.R;
import com.amaze.filemanager.application.AppConfig;
+import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.dialogs.GeneralDialogCreation;
import com.amaze.filemanager.utils.Utils;
import com.google.android.material.snackbar.BaseTransientBottomBar;
@@ -89,8 +91,13 @@ public void onRequestPermissionsResult(
public boolean checkStoragePermission() {
// Verify that all required contact permissions have been granted.
- return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- == PackageManager.PERMISSION_GRANTED;
+ if (SDK_INT >= Build.VERSION_CODES.R) {
+ return ActivityCompat.checkSelfPermission(this, Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED;
+ } else {
+ return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
}
@RequiresApi(api = TIRAMISU)
@@ -192,18 +199,29 @@ private void requestPermission(
} else if (isInitialStart) {
ActivityCompat.requestPermissions(this, new String[] {permission}, code);
} else {
- Snackbar.make(
- findViewById(R.id.content_frame),
- R.string.grantfailed,
- BaseTransientBottomBar.LENGTH_INDEFINITE)
- .setAction(
- R.string.grant,
- v ->
- startActivity(
- new Intent(
- android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.parse(String.format("package:%s", getPackageName())))))
- .show();
+ if (SDK_INT >= Build.VERSION_CODES.R) {
+ Snackbar.make(
+ findViewById(R.id.content_frame),
+ R.string.grantfailed,
+ BaseTransientBottomBar.LENGTH_INDEFINITE)
+ .setAction(
+ R.string.grant,
+ v -> requestAllFilesAccessPermission(onPermissionGranted))
+ .show();
+ } else {
+ Snackbar.make(
+ findViewById(R.id.content_frame),
+ R.string.grantfailed,
+ BaseTransientBottomBar.LENGTH_INDEFINITE)
+ .setAction(
+ R.string.grant,
+ v ->
+ startActivity(
+ new Intent(
+ android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.parse(String.format("package:%s", getPackageName())))))
+ .show();
+ }
}
}
@@ -226,17 +244,7 @@ public void requestAllFilesAccess(@NonNull final OnPermissionGranted onPermissio
.getActionButton(DialogAction.POSITIVE)
.setOnClickListener(
v -> {
- Utils.disableScreenRotation(this);
- permissionCallbacks[ALL_FILES_PERMISSION] = onPermissionGranted;
- try {
- Intent intent =
- new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
- .setData(Uri.parse("package:" + getPackageName()));
- startActivity(intent);
- } catch (Exception e) {
- Log.e(TAG, "Failed to initial activity to grant all files access", e);
- AppConfig.toast(this, getString(R.string.grantfailed));
- }
+ requestAllFilesAccessPermission(onPermissionGranted);
materialDialog.dismiss();
});
materialDialog.setCancelable(false);
@@ -244,6 +252,21 @@ public void requestAllFilesAccess(@NonNull final OnPermissionGranted onPermissio
}
}
+ @RequiresApi(api = Build.VERSION_CODES.R)
+ private void requestAllFilesAccessPermission(@NonNull final OnPermissionGranted onPermissionGranted) {
+ Utils.disableScreenRotation(this);
+ permissionCallbacks[ALL_FILES_PERMISSION] = onPermissionGranted;
+ try {
+ Intent intent =
+ new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
+ .setData(Uri.parse("package:" + getPackageName()));
+ startActivity(intent);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to initial activity to grant all files access", e);
+ AppConfig.toast(this, getString(R.string.grantfailed));
+ }
+ }
+
private boolean isGranted(int[] grantResults) {
return grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
}
From 4432ea712df94ab25baf461e26d066a1a2b6582f Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Fri, 21 Jul 2023 23:30:25 +0800
Subject: [PATCH 054/328] Fix codacy and spotless complaint
Also enable use of jvmToolchain for Kotlin related compilation stuff. See https://youtrack.jetbrains.com/issue/KTIJ-24311/task-current-target-is-17-and-kaptGenerateStubsProductionDebugKotlin-task-current-target-is-1.8-jvm-target-compatibility-should#focus=Comments-27-6798448.0-0 for reason of the workaround
---
app/build.gradle | 5 ++-
.../superclasses/PermissionsActivity.java | 45 ++++++++++---------
file_operations/build.gradle | 8 +++-
portscanner/build.gradle | 12 +++--
4 files changed, 41 insertions(+), 29 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 9bd34bcebd..ee99efc317 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -251,6 +251,10 @@ dependencies {
}
+kotlin {
+ jvmToolchain(11)
+}
+
configurations.all {
resolutionStrategy {
dependencySubstitution {
@@ -305,7 +309,6 @@ tasks.withType(Test) {
jacoco.excludes = ['jdk.internal.*']
}
-
Properties props = new Properties()
def propFile = new File('signing.properties')
diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
index eba49fc7d5..3ca97c186b 100644
--- a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
+++ b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PermissionsActivity.java
@@ -20,8 +20,9 @@
package com.amaze.filemanager.ui.activities.superclasses;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.Build.VERSION.SDK_INT;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.amaze.filemanager.R;
@@ -72,7 +73,7 @@ public void onRequestPermissionsResult(
Toast.makeText(this, R.string.grantfailed, Toast.LENGTH_SHORT).show();
requestStoragePermission(permissionCallbacks[STORAGE_PERMISSION], false);
}
- } else if (requestCode == NOTIFICATION_PERMISSION && Build.VERSION.SDK_INT >= TIRAMISU) {
+ } else if (requestCode == NOTIFICATION_PERMISSION && SDK_INT >= TIRAMISU) {
if (isGranted(grantResults)) {
Utils.enableScreenRotation(this);
} else {
@@ -90,11 +91,12 @@ public void onRequestPermissionsResult(
public boolean checkStoragePermission() {
// Verify that all required contact permissions have been granted.
if (SDK_INT >= Build.VERSION_CODES.R) {
- return ActivityCompat.checkSelfPermission(this, Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
- == PackageManager.PERMISSION_GRANTED;
+ return ActivityCompat.checkSelfPermission(
+ this, Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED;
} else {
return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- == PackageManager.PERMISSION_GRANTED;
+ == PackageManager.PERMISSION_GRANTED;
}
}
@@ -208,23 +210,21 @@ private void requestPermission(
findViewById(R.id.content_frame),
R.string.grantfailed,
BaseTransientBottomBar.LENGTH_INDEFINITE)
- .setAction(
- R.string.grant,
- v -> requestAllFilesAccessPermission(onPermissionGranted))
+ .setAction(R.string.grant, v -> requestAllFilesAccessPermission(onPermissionGranted))
.show();
} else {
Snackbar.make(
- findViewById(R.id.content_frame),
- R.string.grantfailed,
- BaseTransientBottomBar.LENGTH_INDEFINITE)
- .setAction(
- R.string.grant,
- v ->
- startActivity(
- new Intent(
- android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.parse(String.format("package:%s", getPackageName())))))
- .show();
+ findViewById(R.id.content_frame),
+ R.string.grantfailed,
+ BaseTransientBottomBar.LENGTH_INDEFINITE)
+ .setAction(
+ R.string.grant,
+ v ->
+ startActivity(
+ new Intent(
+ android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.parse(String.format("package:%s", getPackageName())))))
+ .show();
}
}
}
@@ -257,13 +257,14 @@ public void requestAllFilesAccess(@NonNull final OnPermissionGranted onPermissio
}
@RequiresApi(api = Build.VERSION_CODES.R)
- private void requestAllFilesAccessPermission(@NonNull final OnPermissionGranted onPermissionGranted) {
+ private void requestAllFilesAccessPermission(
+ @NonNull final OnPermissionGranted onPermissionGranted) {
Utils.disableScreenRotation(this);
permissionCallbacks[ALL_FILES_PERMISSION] = onPermissionGranted;
try {
Intent intent =
- new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
- .setData(Uri.parse("package:" + getPackageName()));
+ new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
+ .setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "Failed to initial activity to grant all files access", e);
diff --git a/file_operations/build.gradle b/file_operations/build.gradle
index 47cefa5e63..1caa72ac33 100644
--- a/file_operations/build.gradle
+++ b/file_operations/build.gradle
@@ -32,8 +32,8 @@ android {
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
testOptions {
@@ -55,6 +55,10 @@ android {
}
}
+kotlin {
+ jvmToolchain(11)
+}
+
dependencies {
modules {
module("org.bouncycastle:bcprov-jdk15to18") {
diff --git a/portscanner/build.gradle b/portscanner/build.gradle
index 355d17f1c3..65b6b3e2f0 100644
--- a/portscanner/build.gradle
+++ b/portscanner/build.gradle
@@ -5,11 +5,11 @@ apply plugin: 'kotlin-parcelize'
android {
namespace 'com.stealthcopter.networktools'
- compileSdk 32
+ compileSdk 33
defaultConfig {
minSdk 14
- targetSdk 32
+ targetSdk 33
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
@@ -22,11 +22,15 @@ android {
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
}
+kotlin {
+ jvmToolchain(11)
+}
+
dependencies {
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
// Because RxAndroid releases are few and far between, it is recommended you also
From 2612b829e55fcf66b865d4251ed0b1dbe7351633 Mon Sep 17 00:00:00 2001
From: Raymond Lai
Date: Sat, 22 Jul 2023 16:00:36 +0800
Subject: [PATCH 055/328] Migrate widgets to use AppCompat versions where
possible
Fixes #3890
---
.../adapters/CompressedExplorerAdapter.java | 8 +-
.../filemanager/adapters/RecyclerAdapter.java | 14 +-
.../adapters/SearchRecyclerViewAdapter.kt | 6 +-
.../glide/AppsAdapterPreloadModel.java | 4 +-
.../filemanager/adapters/holders/AppHolder.kt | 18 +-
.../holders/CompressedItemViewHolder.kt | 18 +-
.../adapters/holders/DonationViewHolder.kt | 8 +-
.../adapters/holders/HiddenViewHolder.kt | 10 +-
.../adapters/holders/ItemViewHolder.kt | 28 +--
.../adapters/holders/SpecialViewHolder.kt | 4 +-
.../asynctasks/CountItemsOrAndSizeTask.java | 6 +-
.../hashcalculator/CalculateHashTask.kt | 8 +-
.../asynctasks/movecopy/PrepareCopyTask.java | 4 +-
.../asynchronous/services/ExtractService.java | 4 +-
.../crashreport/ErrorActivity.java | 26 +--
.../filesystem/files/EncryptDecryptUtils.java | 4 +-
.../com/amaze/filemanager/ui/Extensions.kt | 4 +-
.../ui/activities/AboutActivity.java | 4 +-
.../texteditor/TextEditorActivity.java | 14 +-
.../ui/dialogs/ColorPickerDialog.java | 12 +-
.../ui/dialogs/DecryptFingerprintDialog.kt | 6 +-
.../ui/dialogs/DragAndDropDialog.kt | 10 +-
.../ui/dialogs/GeneralDialogCreation.java | 66 +++---
.../ui/dialogs/SmbConnectDialog.java | 4 +-
.../filemanager/ui/dialogs/SmbSearchDialog.kt | 10 +-
.../ui/dialogs/share/ShareAdapter.java | 8 +-
.../fragments/CompressedExplorerFragment.kt | 14 +-
.../ui/fragments/DbViewerFragment.java | 4 +-
.../ui/fragments/FtpServerFragment.kt | 22 +-
.../ui/fragments/MainFragment.java | 20 +-
.../filemanager/ui/fragments/TabFragment.java | 6 +-
.../BookmarksPrefsFragment.kt | 7 +-
.../ui/selection/SelectionPopupMenu.kt | 5 +-
.../filemanager/ui/views/FastScroller.java | 4 +-
.../filemanager/ui/views/ThemedTextView.java | 4 +-
.../ui/views/WarnableTextInputValidator.java | 6 +-
.../ui/views/appbar/BottomBar.java | 36 ++--
.../ui/views/appbar/SearchView.java | 14 +-
.../views/drawer/ActionViewStateManager.java | 6 +-
.../filemanager/ui/views/drawer/Drawer.java | 17 +-
.../utils/MainActivityActionMode.kt | 5 +-
.../filemanager/utils/MainActivityHelper.java | 19 +-
.../com/amaze/filemanager/utils/Utils.java | 8 +-
app/src/main/res/layout-v16/grid_header.xml | 4 +-
app/src/main/res/layout-v16/list_header.xml | 4 +-
app/src/main/res/layout-v21/layout_appbar.xml | 6 +-
app/src/main/res/layout-v21/layout_search.xml | 8 +-
.../res/layout-w500dp/properties_dialog.xml | 24 +--
.../main/res/layout-w720dp/layout_appbar.xml | 6 +-
.../main/res/layout-w720dp/layout_search.xml | 8 +-
.../main/res/layout-w720dp/main_toolbar.xml | 4 +-
app/src/main/res/layout/actionmode.xml | 4 +-
.../main/res/layout/actionmode_textviewer.xml | 10 +-
app/src/main/res/layout/activity_about.xml | 194 +++++++++---------
app/src/main/res/layout/activity_error.xml | 30 +--
app/src/main/res/layout/bookmarkrow.xml | 5 +-
app/src/main/res/layout/copy_dialog.xml | 6 +-
...log_decrypt_fingerprint_authentication.xml | 2 +-
.../main/res/layout/dialog_twoedittexts.xml | 4 +-
app/src/main/res/layout/drag_placeholder.xml | 4 +-
app/src/main/res/layout/drawerheader.xml | 6 +-
app/src/main/res/layout/fastscroller.xml | 13 +-
app/src/main/res/layout/fragment_app_list.xml | 2 +-
.../main/res/layout/fragment_db_viewer.xml | 2 +-
app/src/main/res/layout/fragment_ftp.xml | 18 +-
.../res/layout/fragment_open_file_dialog.xml | 12 +-
.../main/res/layout/fragment_sheet_cloud.xml | 2 +-
app/src/main/res/layout/grid_header.xml | 4 +-
app/src/main/res/layout/griditem.xml | 9 +-
app/src/main/res/layout/layout_appbar.xml | 6 +-
.../res/layout/layout_draweractionview.xml | 2 +-
app/src/main/res/layout/layout_search.xml | 6 +-
app/src/main/res/layout/lexadrawer.xml | 8 +-
app/src/main/res/layout/list_header.xml | 4 +-
app/src/main/res/layout/main_frag.xml | 10 +-
app/src/main/res/layout/main_toolbar.xml | 4 +-
app/src/main/res/layout/permissiontable.xml | 32 +--
app/src/main/res/layout/processparent.xml | 9 +-
app/src/main/res/layout/properties_audio.xml | 18 +-
app/src/main/res/layout/properties_dialog.xml | 24 +--
.../main/res/layout/properties_document.xml | 62 +++---
.../main/res/layout/properties_general.xml | 30 +--
app/src/main/res/layout/properties_image.xml | 58 +++---
.../res/layout/properties_information.xml | 26 +--
app/src/main/res/layout/properties_video.xml | 18 +-
app/src/main/res/layout/rowlayout.xml | 27 ++-
app/src/main/res/layout/search.xml | 2 +-
app/src/main/res/layout/search_row_item.xml | 4 +-
app/src/main/res/layout/simplerow.xml | 4 +-
app/src/main/res/layout/smb_computers_row.xml | 6 +-
app/src/main/res/layout/smb_dialog.xml | 2 +-
app/src/main/res/layout/snackbar_view.xml | 6 +-
app/src/main/res/layout/tabfragment.xml | 4 +-
.../res/layout/utilities_alias_layout.xml | 8 +-
.../asynctasks/DbViewerTaskTest.java | 11 +-
.../ssh/PemToKeyPairObservableRsaTest.kt | 4 +-
.../ui/activities/TextEditorActivityTest.java | 4 +-
.../views/WarnableTextInputValidatorTest.java | 6 +-
98 files changed, 660 insertions(+), 641 deletions(-)
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/CompressedExplorerAdapter.java b/app/src/main/java/com/amaze/filemanager/adapters/CompressedExplorerAdapter.java
index 3917d23fb9..73b6e2bb07 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/CompressedExplorerAdapter.java
+++ b/app/src/main/java/com/amaze/filemanager/adapters/CompressedExplorerAdapter.java
@@ -53,10 +53,10 @@
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.Toast;
+import androidx.appcompat.widget.AppCompatImageButton;
+import androidx.appcompat.widget.AppCompatImageView;
import androidx.recyclerview.widget.RecyclerView;
/** Created by Arpit on 25-01-2015 edited by Emmanuel Messulam */
@@ -127,7 +127,7 @@ public ArrayList getCheckedItemPositions() {
* @param position the position of the item
* @param imageView the circular {@link CircleGradientDrawable} that is to be animated
*/
- private void toggleChecked(int position, ImageView imageView) {
+ private void toggleChecked(int position, AppCompatImageView imageView) {
compressedExplorerFragment.stopAnim();
stoppedAnimation = true;
@@ -204,7 +204,7 @@ public CompressedItemViewHolder onCreateViewHolder(ViewGroup parent, int viewTyp
} else if (viewType == TYPE_ITEM) {
View v = mInflater.inflate(R.layout.rowlayout, parent, false);
CompressedItemViewHolder vh = new CompressedItemViewHolder(v);
- ImageButton about = v.findViewById(R.id.properties);
+ AppCompatImageButton about = v.findViewById(R.id.properties);
about.setVisibility(View.INVISIBLE);
return vh;
} else {
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java b/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java
index 7e180188ae..bffe1cdd6c 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java
+++ b/app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java
@@ -84,15 +84,15 @@
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import android.widget.ImageView;
import android.widget.PopupMenu;
-import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.view.ContextThemeWrapper;
+import androidx.appcompat.widget.AppCompatImageView;
+import androidx.appcompat.widget.AppCompatTextView;
import androidx.recyclerview.widget.RecyclerView;
/**
@@ -200,7 +200,7 @@ public RecyclerAdapter(
* @param position the position of the item
* @param imageView the check {@link CircleGradientDrawable} that is to be animated
*/
- public void toggleChecked(int position, ImageView imageView) {
+ public void toggleChecked(int position, AppCompatImageView imageView) {
if (getItemsDigested().size() <= position || position < 0) {
AppConfig.toast(context, R.string.operation_not_supported);
return;
@@ -1192,7 +1192,7 @@ private View getDragShadow(int selectionCount) {
.setVisibility(View.VISIBLE);
String rememberMovePreference =
sharedPrefs.getString(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_REMEMBERED, "");
- ImageView icon =
+ AppCompatImageView icon =
mainFragment
.getMainActivity()
.getTabFragment()
@@ -1204,7 +1204,7 @@ private View getDragShadow(int selectionCount) {
.getTabFragment()
.getDragPlaceholder()
.findViewById(R.id.files_count_parent);
- TextView filesCount =
+ AppCompatTextView filesCount =
mainFragment
.getMainActivity()
.getTabFragment()
@@ -1238,7 +1238,7 @@ private int getDragIconReference(String rememberMovePreference) {
private void showThumbnailWithBackground(
ItemViewHolder viewHolder,
IconDataParcelable iconData,
- ImageView view,
+ AppCompatImageView view,
OnImageProcessed errorListener) {
if (iconData.isImageBroken()) {
viewHolder.genericIcon.setVisibility(View.VISIBLE);
@@ -1301,7 +1301,7 @@ public boolean onResourceReady(
private void showRoundedThumbnail(
ItemViewHolder viewHolder,
IconDataParcelable iconData,
- ImageView view,
+ AppCompatImageView view,
OnImageProcessed errorListener) {
if (iconData.isImageBroken()) {
View iconBackground =
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
index c523453385..d9ea4c87ce 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/SearchRecyclerViewAdapter.kt
@@ -24,7 +24,7 @@ import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
+import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
@@ -81,8 +81,8 @@ class SearchRecyclerViewAdapter :
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
- val fileNameTV: TextView
- val filePathTV: TextView
+ val fileNameTV: AppCompatTextView
+ val filePathTV: AppCompatTextView
val colorView: View
init {
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/glide/AppsAdapterPreloadModel.java b/app/src/main/java/com/amaze/filemanager/adapters/glide/AppsAdapterPreloadModel.java
index 53d98db748..c76d130cfc 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/glide/AppsAdapterPreloadModel.java
+++ b/app/src/main/java/com/amaze/filemanager/adapters/glide/AppsAdapterPreloadModel.java
@@ -36,10 +36,10 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
@@ -85,7 +85,7 @@ public RequestBuilder getPreloadRequestBuilder(String item) {
}
}
- public void loadApkImage(String item, ImageView v) {
+ public void loadApkImage(String item, AppCompatImageView v) {
if (isBottomSheet) {
request.load(getApplicationIconFromPackageName(item)).into(v);
} else {
diff --git a/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt b/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
index f5ef96a37f..71edf51820 100644
--- a/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
+++ b/app/src/main/java/com/amaze/filemanager/adapters/holders/AppHolder.kt
@@ -22,10 +22,10 @@ package com.amaze.filemanager.adapters.holders
import android.view.View
import android.view.ViewGroup
-import android.widget.ImageButton
-import android.widget.ImageView
import android.widget.RelativeLayout
-import android.widget.TextView
+import androidx.appcompat.widget.AppCompatImageButton
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.appcompat.widget.AppCompatTextView
import androidx.core.view.marginBottom
import androidx.core.view.marginLeft
import androidx.core.view.marginTop
@@ -36,7 +36,7 @@ import com.amaze.filemanager.utils.Utils
class AppHolder(view: View) : RecyclerView.ViewHolder(view) {
@JvmField
- val apkIcon: ImageView = view.findViewById(R.id.apk_icon)
+ val apkIcon: AppCompatImageView = view.findViewById(R.id.apk_icon)
@JvmField
val txtTitle: ThemedTextView = view.findViewById(R.id.firstline)
@@ -45,16 +45,16 @@ class AppHolder(view: View) : RecyclerView.ViewHolder(view) {
val rl: RelativeLayout = view.findViewById(R.id.second)
@JvmField
- val txtDesc: TextView = view.findViewById(R.id.date)
+ val txtDesc: AppCompatTextView = view.findViewById(R.id.date)
@JvmField
- val about: ImageButton = view.findViewById(R.id.properties)
+ val about: AppCompatImageButton = view.findViewById(R.id.properties)
@JvmField
val summary: RelativeLayout = view.findViewById(R.id.summary)
@JvmField
- val packageName: TextView = view.findViewById(R.id.appManagerPackageName)
+ val packageName: AppCompatTextView = view.findViewById(R.id.appManagerPackageName)
init {
apkIcon.visibility = View.VISIBLE
@@ -69,7 +69,7 @@ class AppHolder(view: View) : RecyclerView.ViewHolder(view) {
)
txtDesc.layoutParams = layoutParams
- view.findViewById(R.id.picture_icon).visibility = View.GONE
- view.findViewById