implementation 'com.google.android.material:material:1.3.0'
// Only compile AdMob ads for the free flavor.
- freeImplementation 'com.google.android.gms:play-services-ads:19.8.0'
+ freeImplementation 'com.google.android.gms:play-services-ads:20.0.0'
}
\ No newline at end of file
<hr/>
<br/>
- <p>In addition, Privacy Browser Free displays ads from Google’s AdMob network. For the free flavor, AdMob adds the following permissions even though they are not listed in the source code
- <a href="https://gitweb.stoutner.com/?p=PrivacyBrowser.git;a=blob;f=app/src/main/AndroidManifest.xml;hb=HEAD">manifest file</a>.</p>
+ <p>En outre, Privacy Browser Free affiche des publicités provenant du réseau AdMob de Google.
+ Pour la version gratuite, AdMob ajoute les autorisations suivantes, même si elles ne sont pas répertoriées dans le code source
+ <a href="https://gitweb.stoutner.com/?p=PrivacyBrowser.git;a=blob;f=app/src/main/AndroidManifest.xml;hb=HEAD">fichier de manifeste</a>.</p>
<h3>Afficher les connexions réseau</h3>
<p><a href="https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_NETWORK_STATE">android.permission.ACCESS_NETWORK_STATE</a></p>
<p><a href="https://developer.android.com/reference/android/Manifest.permission.html#WAKE_LOCK">android.permission.WAKE_LOCK</a></p>
<p>Permet aux annonces d'empêcher le processeur de dormir et l'éclairage de l'écran, bien que lors de mes tests, je ne pense pas que les annonces le fassent réellement.</p>
- <h3>Run at startup</h3>
+ <h3>Lancer au démarrage</h3>
<p><a href="https://developer.android.com/reference/android/Manifest.permission.html#RECEIVE_BOOT_COMPLETED">android.permission.RECEIVE_BOOT_COMPLETED</a></p>
- <p>Lets AdMob start when the phone boots even if you don't open Privacy Browser Free. This is a concerning permission because it can allow Google to spy on you.
- I would either like to find a different ad provider or drop the free flavor of Privacy Browser entirely.</p>
+ <p>Laisser AdMob démarrer au démarrage du téléphone même si vous n'ouvrez pas Privacy Browser Free. C'est une permission inquiétante car elle peut permettre à Google de vous espionner.
+ J'aimerais soit trouver un autre fournisseur de publicité, soit abandonner entièrement la version gratuite de Privacy Browser.</p>
</body>
</html>
\ No newline at end of file
<!-- Orbot. -->
<package android:name="org.torproject.android" />
- <!-- For some reason, OpenKeychain is visible without having to be listed in the queries. -->
+ <!-- OpenKeyChain. -->
+ <package android:name="org.sufficientlysecure.keychain" />
</queries>
<!-- For API >= 23, app data is automatically backed up to Google cloud servers unless `android:allowBackup="false"` and `android:fullBackupContent="false"` is set. -->
<data android:scheme="https" />
</intent-filter>
- <!-- Process content intents for text files. -->
+ <!-- Process all content intents, including text, images, and MHT archives. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
- <data android:mimeType="text/*" />
+ <data android:mimeType="*/*" />
</intent-filter>
<!-- Process intents for text strings. Sometimes URLs are presented this way. -->
<!--
Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
- Translation 2019-2020 Kévin L. <kevinliste@framalistes.org>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+ Translation 2019-2021 Kévin L. <kevinliste@framalistes.org>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<h3><a href="https://www.stoutner.com/privacy-browser-3-7/">3.7</a> (version du code 54)</h3>
<p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=f3b9172adedd74f705ddc0beac80798ae84f2920">29 Mars 2021</a> - API minimale : 19, API optimale : 30</p>
<ul>
- <li>Redesign file access to work with <a href="https://redmine.stoutner.com/issues/546">scoped storage and the Storage Access Framework</a>.
- This allows the target API to be bumped to 30 and removes the need for the dangerous READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions.
- Unfortunately, due to a bug in Android’s WebView, this also temporarily removes the ability to <a href="https://redmine.stoutner.com/issues/677">save a web archive</a>.</li>
- <li>Update <a href="https://redmine.stoutner.com/issues/678">About > Permissions</a>.</li>
- <li>Improve the descriptiveness of the <a href="https://redmine.stoutner.com/issues/676">save URL snackbar</a>.</li>
- <li>Add <a href="https://redmine.stoutner.com/issues/568">Metager</a> to the list of search engines.</li>
- <li>Fix <a href="https://redmine.stoutner.com/issues/674">I2P detection</a>.</li>
- <li>Fix the alignment of <a href="https://redmine.stoutner.com/issues/228">icons and radio buttons</a> in the dialogs.</li>
- <li>Update the URL bar when switching tabs <a href="https://redmine.stoutner.com/issues/654">even if it is being edited</a>.</li>
- <li>Allow <a href="https://redmine.stoutner.com/issues/620">displaying of the password</a> in the HTTP authentication dialog.</li>
- <li>Fix a <a href="https://redmine.stoutner.com/issues/645">number</a> <a href="https://redmine.stoutner.com/issues/646">of</a> <a href="https://redmine.stoutner.com/issues/651">rare</a>
- <a href="https://redmine.stoutner.com/issues/663">crashes</a>.</li>
- <li>Fix the hamburger icon <a href="https://redmine.stoutner.com/issues/616">turning into an arrow</a> if the drawer is open when the app is restarted.</li>
- <li>Speed up the opening of the <a href="https://redmine.stoutner.com/issues/650">options menu</a>.</li>
+ <li>Reconception de l'accès aux fichiers pour travailler avec <a href="https://redmine.stoutner.com/issues/546">le stockage cible et le Storage Access Framework</a>.
+ Cela permet de faire passer l'API cible à 30 et de supprimer la nécessité des permissions dangereuses READ_EXTERNAL_STORAGE et WRITE_EXTERNAL_STORAGE.
+ Malheureusement, en raison d'un bogue dans la WebView d'Android, cela supprime aussi temporairement la possibilité
+ <a href="https://redmine.stoutner.com/issues/677">d'enregistrer une archive web</a>.</li>
+ <li>Mise à jour des <a href="https://redmine.stoutner.com/issues/678">A propos de > permissions</a>.</li>
+ <li>Amélioration de la description de la <a href="https://redmine.stoutner.com/issues/676">bare d'enregistrement d'URL</a>.</li>
+ <li>Ajout de <a href="https://redmine.stoutner.com/issues/568">Metager</a> à la liste des moteurs de recherche.</li>
+ <li>Correction de <a href="https://redmine.stoutner.com/issues/674">détection I2P</a>.</li>
+ <li>Correction de l'alignement des <a href="https://redmine.stoutner.com/issues/228">icônes et boutons radio</a> dans les boîtes de dialogue.</li>
+ <li>Mise à jour de la barre d'URL lors du changement d'onglet <a href="https://redmine.stoutner.com/issues/654">même s'il est en cours d'édition</a>.</li>
+ <li>Autorisation de <a href="https://redmine.stoutner.com/issues/620">l'affichage du mot de passe</a> dans le dialogue d'authentification HTTP.</li>
+ <li>Correction d'un <a href="https://redmine.stoutner.com/issues/645">nombre</a> <a href="https://redmine.stoutner.com/issues/646">de</a> <a href="https://redmine.stoutner.com/issues/651">rare</a>
+ <a href="https://redmine.stoutner.com/issues/663">crash</a>.</li>
+ <li>Correction de l'icône du hamburger <a href="https://redmine.stoutner.com/issues/616">se transformant en flèche</a> si le tiroir est ouvert lors du redémarrage de l'app.</li>
+ <li>Accélération de l'ouverture du <a href="https://redmine.stoutner.com/issues/650">menu des options</a>.</li>
<li>Traduction française mise à jour fournie par <a href="mailto:kevinliste@framalistes.org">Kévin L</a>.</li>
- <li>Updated Brazilian Portuguese translation provided by <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>.</li>
+ <li>Traduction portugaise brésilienne mise à jour fournie par <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>.</li>
<li>Traduction allemande mise à jour fournie par Bernhard G. Keller.</li>
<li>Traduction italienne mise à jour fournie par Francesco Buratti.</li>
<li>Traduction russe mise à jour.</li>
// Close the streams.
inputStream.close();
- temporaryPgpEncryptedImportFileOutputStream.flush();
-
+ temporaryPgpEncryptedImportFileOutputStream.close();
// Create an decryption intent for OpenKeychain.
Intent openKeychainDecryptIntent = new Intent("org.sufficientlysecure.keychain.action.DECRYPT_DATA");
import android.webkit.WebViewClient;
import android.webkit.WebViewDatabase;
import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
// Consume the event.
+ return true;
+ } else if (menuItemId == R.id.save_archive) {
+ // Instantiate the save dialog. TODO. Replace the hard coded file name.
+ DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_ARCHIVE, null, null, "Webpage.mht", null,
+ false);
+
+ // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name.
+ saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
+
return true;
} else if (menuItemId == R.id.save_image) { // Save image.
// Instantiate the save dialog.
saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
// Consume the event.
- return true;
- } else if (menuItemId == R.id.save_archive) {
- /* TODO.
- try {
- // Create an MHT file.
- File mhtFile = File.createTempFile("mht_file", ".mht", getCacheDir());
-
- // Populate the MHT file.
- currentWebView.saveWebArchive(mhtFile.toString());
-
- // Check the file length.
- Log.i("MHT", "MHT file size: " + mhtFile.length());
- } catch (Exception exception){
- Log.i("MHT", "MHT exception: " + exception.toString());
- }
-
- */
-
return true;
} else if (menuItemId == R.id.add_to_homescreen) { // Add to homescreen.
// Instantiate the create home screen shortcut dialog.
// Remove the incorrect lint warning below that the dialog might be null.
assert dialog != null;
- // Get a handle for the file name edit text.
+ // Get handles for the views.
EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
+ CheckBox mhtCheckBox = dialog.findViewById(R.id.mht_checkbox);
// Get the file path string.
String openFilePath = fileNameEditText.getText().toString();
// Apply the domain settings. This resets the favorite icon and removes any domain settings.
applyDomainSettings(currentWebView, openFilePath, true, false, false);
- // Open the file.
- currentWebView.loadUrl(openFilePath);
+ // Open the file according to the type.
+ if (mhtCheckBox.isChecked()) { // Force opening of an MHT file.
+ try {
+ // Get the MHT file input stream.
+ InputStream mhtFileInputStream = getContentResolver().openInputStream(Uri.parse(openFilePath));
+
+ // Create a temporary MHT file.
+ File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir());
+
+ // Get a file output stream for the temporary MHT file.
+ FileOutputStream temporaryMhtFileOutputStream = new FileOutputStream(temporaryMhtFile);
+
+ // Create a transfer byte array.
+ byte[] transferByteArray = new byte[1024];
+
+ // Create an integer to track the number of bytes read.
+ int bytesRead;
+
+ // Copy the temporary MHT file input stream to the MHT output stream.
+ while ((bytesRead = mhtFileInputStream.read(transferByteArray)) > 0) {
+ temporaryMhtFileOutputStream.write(transferByteArray, 0, bytesRead);
+ }
+
+ // Flush the temporary MHT file output stream.
+ temporaryMhtFileOutputStream.flush();
+
+ // Close the streams.
+ temporaryMhtFileOutputStream.close();
+ mhtFileInputStream.close();
+
+ // Load the temporary MHT file.
+ currentWebView.loadUrl(temporaryMhtFile.toString());
+ } catch (Exception exception) {
+ // Display a snackbar.
+ Snackbar.make(currentWebView, getString(R.string.error) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show();
+ }
+ } else { // Let the WebView handle opening of the file.
+ // Open the file.
+ currentWebView.loadUrl(openFilePath);
+ }
}
@Override
new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
break;
+ case SaveWebpageDialog.SAVE_ARCHIVE:
+ try {
+ // Create a temporary MHT file.
+ File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir());
+
+ // Save the temporary MHT file.
+ currentWebView.saveWebArchive(temporaryMhtFile.toString(), false, callbackValue -> {
+ if (callbackValue != null) { // The temporary MHT file was saved successfully.
+ try {
+ // Create a temporary MHT file input stream.
+ FileInputStream temporaryMhtFileInputStream = new FileInputStream(temporaryMhtFile);
+
+ // Get an output stream for the save webpage file path.
+ OutputStream mhtOutputStream = getContentResolver().openOutputStream(Uri.parse(saveWebpageFilePath));
+
+ // Create a transfer byte array.
+ byte[] transferByteArray = new byte[1024];
+
+ // Create an integer to track the number of bytes read.
+ int bytesRead;
+
+ // Copy the temporary MHT file input stream to the MHT output stream.
+ while ((bytesRead = temporaryMhtFileInputStream.read(transferByteArray)) > 0) {
+ mhtOutputStream.write(transferByteArray, 0, bytesRead);
+ }
+
+ // Close the streams.
+ mhtOutputStream.close();
+ temporaryMhtFileInputStream.close();
+
+ // Display a snackbar.
+ Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + saveWebpageFilePath, Snackbar.LENGTH_SHORT).show();
+ } catch (Exception exception) {
+ // Display a snackbar with the exception.
+ Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show();
+ } finally {
+ // Delete the temporary MHT file.
+ //noinspection ResultOfMethodCallIgnored
+ temporaryMhtFile.delete();
+ }
+ } else { // There was an unspecified error while saving the temporary MHT file.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.error_saving_file), Snackbar.LENGTH_INDEFINITE).show();
+ }
+ });
+ } catch (IOException ioException) {
+ // Display a snackbar with the IO exception.
+ Snackbar.make(currentWebView, getString(R.string.error_saving_file) + " " + ioException.toString(), Snackbar.LENGTH_INDEFINITE).show();
+ }
+ break;
+
case SaveWebpageDialog.SAVE_IMAGE:
// Save the webpage image.
new SaveWebpageImage(this, saveWebpageFilePath, currentWebView).execute();
// Explicitly disable geolocation.
nestedScrollWebView.getSettings().setGeolocationEnabled(false);
+ // Allow loading of file:// URLs. This is necessary for opening MHT web archives, which are copies into a temporary cache location.
+ nestedScrollWebView.getSettings().setAllowFileAccess(true);
+
// Create a double-tap gesture detector to toggle full-screen mode.
GestureDetector doubleTapGestureDetector = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener() {
// Override `onDoubleTap()`. All other events are handled using the default settings.
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
+import android.view.View
import android.view.WindowManager
import android.widget.Button
+import android.widget.CheckBox
import android.widget.EditText
+import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.stoutner.privacybrowser.R
import com.stoutner.privacybrowser.activities.MainWebViewActivity
+// Define the class constants.
+private const val MHT_EXPLANATION_VISIBILITY = "mht_explanation_visibility"
+
class OpenDialog : DialogFragment() {
- // Define the open listener.
+ // Declare the class variables.
private lateinit var openListener: OpenListener
+ // Declare the class views.
+ private lateinit var mhtExplanationTextView: TextView
+
// The public interface is used to send information back to the parent activity.
interface OpenListener {
fun onOpen(dialogFragment: DialogFragment)
// Get handles for the layout items.
val fileNameEditText = alertDialog.findViewById<EditText>(R.id.file_name_edittext)!!
val browseButton = alertDialog.findViewById<Button>(R.id.browse_button)!!
+ val mhtCheckBox = alertDialog.findViewById<CheckBox>(R.id.mht_checkbox)!!
+ mhtExplanationTextView = alertDialog.findViewById(R.id.mht_explanation_textview)!!
val openButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
// Initially disable the open button.
requireActivity().startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_OPEN_REQUEST_CODE)
}
+ // Handle clicks on the MHT checkbox.
+ mhtCheckBox.setOnClickListener {
+ // Update the visibility of the MHT explanation text view.
+ if (mhtCheckBox.isChecked) {
+ mhtExplanationTextView.visibility = View.VISIBLE
+ } else {
+ mhtExplanationTextView.visibility = View.GONE
+ }
+ }
+
+ // Restore the MHT explanation text view visibility if the saved instance state is not null.
+ if (savedInstanceState != null) {
+ // Restore the MHT explanation text view visibility.
+ if (savedInstanceState.getBoolean(MHT_EXPLANATION_VISIBILITY)) {
+ mhtExplanationTextView.visibility = View.VISIBLE
+ } else {
+ mhtExplanationTextView.visibility = View.GONE
+ }
+ }
+
// Return the alert dialog.
return alertDialog
}
+
+ override fun onSaveInstanceState(savedInstanceState: Bundle) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState)
+
+ // Add the MHT explanation visibility status to the bundle.
+ savedInstanceState.putBoolean(MHT_EXPLANATION_VISIBILITY, mhtExplanationTextView.visibility == View.VISIBLE)
+ }
}
\ No newline at end of file
companion object {
// Define the companion object constants. These can be moved to class constants once all of the code has transitioned to Kotlin.
const val SAVE_URL = 0
- const val SAVE_IMAGE = 1
+ const val SAVE_ARCHIVE = 1
+ const val SAVE_IMAGE = 2
// `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
@JvmStatic
}
}
+ SAVE_ARCHIVE -> {
+ // Set the title.
+ dialogBuilder.setTitle(R.string.save_archive)
+
+ // Set the icon according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ dialogBuilder.setIcon(R.drawable.dom_storage_cleared_day)
+ } else {
+ dialogBuilder.setIcon(R.drawable.dom_storage_cleared_night)
+ }
+ }
+
SAVE_IMAGE -> {
// Set the title.
dialogBuilder.setTitle(R.string.save_image)
// Define the class constants.
private const val TAB_NUMBER = "tab_number"
+private const val SCROLL_X = "scroll_x"
+private const val SCROLL_Y = "scroll_y"
class AboutWebViewFragment : Fragment() {
// Define the class variables.
// Scroll the tab if the saved instance state is not null.
if (savedInstanceState != null) {
tabWebView.post {
- tabWebView.scrollX = savedInstanceState.getInt("scroll_x")
- tabWebView.scrollY = savedInstanceState.getInt("scroll_y")
+ tabWebView.scrollX = savedInstanceState.getInt(SCROLL_X)
+ tabWebView.scrollY = savedInstanceState.getInt(SCROLL_Y)
}
}
// Save the scroll positions if the layout is not null, which can happen if a tab is not currently selected.
if (tabWebView != null) {
- savedInstanceState.putInt("scroll_x", tabWebView.scrollX)
- savedInstanceState.putInt("scroll_y", tabWebView.scrollY)
+ savedInstanceState.putInt(SCROLL_X, tabWebView.scrollX)
+ savedInstanceState.putInt(SCROLL_Y, tabWebView.scrollY)
}
}
}
\ No newline at end of file
android:layout_height="wrap_content"
android:layout_width="match_parent" >
- <!-- Align the edit text and the select file button horizontally. -->
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:orientation="horizontal"
- android:layout_marginTop="10dp"
- android:layout_marginStart="10dp"
- android:layout_marginEnd="10dp" >
+ android:orientation="vertical" >
- <!-- The text input layout makes the `android:hint` float above the edit text. -->
- <com.google.android.material.textfield.TextInputLayout
+ <!-- Align the edit text and the select file button horizontally. -->
+ <LinearLayout
android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:layout_weight="1" >
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp" >
- <!-- `android:inputType="textUri"` disables spell check and places an `/` on the main keyboard. -->
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/file_name_edittext"
+ <!-- The text input layout makes the `android:hint` float above the edit text. -->
+ <com.google.android.material.textfield.TextInputLayout
android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:hint="@string/file_name"
- android:inputType="textMultiLine|textUri" />
- </com.google.android.material.textfield.TextInputLayout>
+ android:layout_width="0dp"
+ android:layout_weight="1" >
- <Button
- android:id="@+id/browse_button"
+ <!-- `android:inputType="textUri"` disables spell check and places an `/` on the main keyboard. -->
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/file_name_edittext"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:hint="@string/file_name"
+ android:inputType="textMultiLine|textUri" />
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <Button
+ android:id="@+id/browse_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/browse" />
+ </LinearLayout>
+
+ <CheckBox
+ android:id="@+id/mht_checkbox"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/file_is_mht"
+ android:layout_marginStart="8dp" />
+
+ <TextView
+ android:id="@+id/mht_explanation_textview"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/browse" />
+ android:layout_width="match_parent"
+ android:text="@string/mht_checkbox_explanation"
+ android:textColor="?android:textColorPrimary"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center_horizontal"
+ android:visibility="gone" />
</LinearLayout>
</ScrollView>
\ No newline at end of file
android:orderInCategory="1201"
app:showAsAction="never" />
- <!-- TODO. -->
<item
android:id="@+id/save_archive"
- android:title="Save Archive"
+ android:title="@string/save_archive"
android:orderInCategory="1202"
app:showAsAction="never" />
<!-- Save Dialogs. -->
<string name="save_url">URL speichern</string>
+ <string name="save_archive">Archiv speichern</string>
<string name="save_text">Text speichern</string>
<string name="save_image">Grafik speichern</string>
<string name="save_logcat">Logcat speichern</string>
<!-- Save Dialogs. -->
<string name="save_url">Guardar URL</string>
+ <string name="save_archive">Guardar archivo</string>
<string name="save_text">Guardar texto</string>
<string name="save_image">Guardar imagen</string>
<string name="save_logcat">Guardar logcat</string>
<!-- Save Dialogs. -->
<string name="save_url">Enregistrer l\'URL</string>
+ <string name="save_archive">Enregistrer l\'archive</string>
<string name="save_text">Sauvegarder le texte</string>
<string name="save_image">Sauvegarder en tant qu\'image</string>
<string name="save_logcat">Sauvegarder le journal système</string>
<string name="bytes">octets</string>
<string name="unknown_size">taille inconnue</string>
<string name="invalid_url">URL invalide</string>
- <string name="saving_file">Enregistrement du fichier:</string>
- <string name="file_saved">Fichier enregistré:</string>
+ <string name="saving_file">Enregistrement du fichier :</string>
+ <string name="file_saved">Fichier enregistré :</string>
<string name="processing_image">Traitement de l\'image… :</string>
- <string name="error_saving_file">Erreur lors de l\'enregistrement du fichier:</string>
+ <string name="image_saved">Image sauvegardée :</string>
+ <string name="error_saving_file">Erreur lors de l\'enregistrement du fichier :</string>
<!-- View Source. -->
<string name="request_headers">En-tête de la requête</string>
<!-- Save Dialogs. -->
<string name="save_url">Salva URL</string>
+ <string name="save_archive">Salva Archivio</string>
<string name="save_text">Salva Testo</string>
<string name="save_image">Salva Immagine</string>
<string name="save_logcat">Salva il log</string>
<!-- Save Dialogs. -->
<string name="save_url">Salvar URL</string>
+ <string name="save_archive">Salvar Arquivo</string>
<string name="save_text">Salvar Texto</string>
<string name="save_image">Salvar Imagem</string>
<string name="save_logcat">Salvar logcat</string>
<!-- Save Dialogs. -->
<string name="save_url">Сохранить URL</string>
+ <string name="save_archive">Сохранить архив</string>
<string name="save_text">Сохранить текст</string>
<string name="save_image">Сохранить изображение</string>
<string name="save_logcat">Сохранить logcat</string>
<string name="previous">Previous</string>
<string name="next">Next</string>
+ <!-- Open Dialog. -->
+ <string name="file_is_mht">The file is an MHT web archive.</string>
+ <string name="mht_checkbox_explanation">Sometimes MIME Encapsulated HTML (MHT) web archives need to be manually specified to be opened correctly.</string>
+
<!-- Save Dialogs. -->
<string name="save_dialog" translatable="false">Save Dialog</string> <!-- This string is used to tag the save dialog. It is never displayed to the user. -->
<string name="save_url">Save URL</string>
+ <string name="save_archive">Save Archive</string>
<string name="save_text">Save Text</string>
<string name="save_image">Save Image</string>
<string name="save_logcat">Save Logcat</string>
<item>WebView default user agent</item> <!-- This item must not be translated into other languages because it is referenced in code. It is never displayed on the screen. -->
<item>Mozilla/5.0 (Android 11; Mobile; rv:87.0) Gecko/87.0 Firefox/87.0</item>
<item>Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36</item>
- <item>Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1</item>
+ <item>Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1</item>
<item>Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0</item>
<item>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36</item>
<item>Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0</item>
<item>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36</item>
<item>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63</item>
<item>Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko</item>
- <item>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15</item>
+ <item>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15</item>
<item>Custom user agent</item> <!-- This item must not be translated into other languages because it is referenced in code. It is never displayed on the screen. -->
</string-array>
<string name="custom_user_agent">Custom user agent</string>
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2018 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2018,2021 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<cache-path
name="private-cache-directory"
path="." />
-
- <external-files-path
- name="external-app-directories"
- path="." />
-
- <external-path
- name="external-directories"
- path="." />
</paths>
\ No newline at end of file
-• Redesign file access to work with scoped storage and the Storage Access Framework. This allows the target API to be bumped to 30 and removes the need for the dangerous READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions. Unfortunately, due to a bug in Android’s WebView, this also temporarily removes the ability to save a web archive.
-• Update About > Permissions.
-• Improve the descriptiveness of the save URL snackbar.
-• Add Metager to the list of search engines.
-• Fix I2P detection.
-• Fix the alignment of icons and radio buttons in the dialogs.
-• Update the URL bar when switching tabs even if it is being edited.
-• Allow displaying of the password in the HTTP authentication dialog.
-• Fix a number of rare crashes.
-• Fix the hamburger icon turning into an arrow if the drawer is open when the app is restarted.
-• Speed up the opening of the options menu.
+• Reconception de l'accès aux fichiers pour travailler avec le stockage cible et le Storage Access Framework. Cela permet de faire passer l'API cible à 30 et de supprimer la nécessité des permissions dangereuses READ_EXTERNAL_STORAGE et WRITE_EXTERNAL_STORAGE. Malheureusement, en raison d'un bogue dans la WebView d'Android, cela supprime aussi temporairement la possibilité d'enregistrer une archive web.
+• Mise à jour des A propos de > permissions.
+• Amélioration de la description de la bare d'enregistrement d'URL.
+• Ajout de Metager à la liste des moteurs de recherche.
+• Correction de détection I2P.
+• Correction de l'alignement des icônes et boutons radio dans les boîtes de dialogue.
+• Mise à jour de la barre d'URL lors du changement d'onglet même s'il est en cours d'édition.
+• Autorisation de l'affichage du mot de passe dans le dialogue d'authentification HTTP.
+• Correction d'un nombre de rare crash.
+• Correction de l'icône du hamburger se transformant en flèche si le tiroir est ouvert lors du redémarrage de l'app.
+• Accélération de l'ouverture du menu des options.
• Traduction française mise à jour fournie par Kévin L.
-• Updated Brazilian Portuguese translation provided by Thiago Nazareno Conceição Silva de Jesus.
+• Traduction portugaise brésilienne mise à jour fournie par Thiago Nazareno Conceição Silva de Jesus.
• Traduction allemande mise à jour fournie par Bernhard G. Keller.
• Traduction italienne mise à jour fournie par Francesco Buratti.
• Traduction russe mise à jour.