diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 2a79aa3ca..7b3347083 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -28,6 +28,7 @@ DeltaLab was created with a focus on usability, good user experience, and saving
  • Can be selected as app to open .xdc files
  • For mini-apps developers: there are some differences in the WebXDC API, check https://github.com/adbenitez/deltalab-android/#webxdc
  • Send and display WebXDC of up to 10MB
  • +
  • Additional "Privacy" section in the settings
  • WARNING: Be careful when running WebXDC apps in DeltaLab, the sandboxed webview can't currently prevent WebRTC connections, so in theory information inside the WebXDC app could be leaked and also your IP, the risk is low, for most people this is not even important, but if you are a person at risk, only use trusted apps, or use official Delta Chat. diff --git a/res/drawable/ic_lock_24dp.xml b/res/drawable/ic_lock_24dp.xml new file mode 100644 index 000000000..da2a77be2 --- /dev/null +++ b/res/drawable/ic_lock_24dp.xml @@ -0,0 +1,4 @@ + + diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index fa1624b8c..b48fac2e7 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -10,6 +10,11 @@ android:icon="@drawable/ic_forum_24dp" android:layout="@layout/preference_item" /> + + - - - - - - - - - - - - - - - - - - - diff --git a/res/xml/preferences_privacy.xml b/res/xml/preferences_privacy.xml new file mode 100644 index 000000000..cdf1e1d79 --- /dev/null +++ b/res/xml/preferences_privacy.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java index 75e96d619..3bf20d1ca 100644 --- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java +++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java @@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment; import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment; import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment; +import org.thoughtcrime.securesms.preferences.PrivacyPreferenceFragment; import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment; import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference; @@ -70,6 +71,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "preference_category_notifications"; private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance"; private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats"; + private static final String PREFERENCE_CATEGORY_PRIVACY = "preference_category_privacy"; private static final String PREFERENCE_CATEGORY_MULTIDEVICE = "preference_category_multidevice"; private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced"; private static final String PREFERENCE_CATEGORY_CONNECTIVITY = "preference_category_connectivity"; @@ -147,6 +149,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE)); this.findPreference(PREFERENCE_CATEGORY_CHATS) .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS)); + this.findPreference(PREFERENCE_CATEGORY_PRIVACY) + .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_PRIVACY)); this.findPreference(PREFERENCE_CATEGORY_MULTIDEVICE) .setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_MULTIDEVICE)); this.findPreference(PREFERENCE_CATEGORY_ADVANCED) @@ -198,6 +202,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA .setSummary(AppearancePreferenceFragment.getSummary(getActivity())); this.findPreference(PREFERENCE_CATEGORY_CHATS) .setSummary(ChatsPreferenceFragment.getSummary(getActivity())); + this.findPreference(PREFERENCE_CATEGORY_PRIVACY) + .setSummary(PrivacyPreferenceFragment.getSummary(getActivity())); this.findPreference(PREFERENCE_CATEGORY_CONNECTIVITY) .setSummary(DcHelper.getConnectivitySummary(getActivity(), R.string.connectivity_connected)); this.findPreference(PREFERENCE_CATEGORY_HELP) @@ -210,6 +216,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA Drawable swap = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_swap_vert_white_24dp)); Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp)); Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp)); + Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_lock_white_24dp)); Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp)); Drawable help = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_help_white_24dp)); @@ -222,6 +229,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA DrawableCompat.setTint(swap, color); DrawableCompat.setTint(appearance, color); DrawableCompat.setTint(chats, color); + DrawableCompat.setTint(privacy, color); DrawableCompat.setTint(advanced, color); DrawableCompat.setTint(help, color); @@ -229,6 +237,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA this.findPreference(PREFERENCE_CATEGORY_CONNECTIVITY).setIcon(swap); this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance); this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats); + this.findPreference(PREFERENCE_CATEGORY_PRIVACY).setIcon(privacy); this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced); this.findPreference(PREFERENCE_CATEGORY_HELP).setIcon(help); } @@ -267,6 +276,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA case PREFERENCE_CATEGORY_CHATS: fragment = new ChatsPreferenceFragment(); break; + case PREFERENCE_CATEGORY_PRIVACY: + fragment = new PrivacyPreferenceFragment(); + break; case PREFERENCE_CATEGORY_MULTIDEVICE: if (!ScreenLockUtil.applyScreenLock(getActivity(), getString(R.string.multidevice_title), getString(R.string.multidevice_this_creates_a_qr_code) + "\n\n" + getString(R.string.enter_system_secret_to_continue), diff --git a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java index 7975dc098..74e7b66b0 100644 --- a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java @@ -19,7 +19,6 @@ import android.util.Log; import android.view.View; import android.webkit.WebView; import android.widget.EditText; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,7 +35,6 @@ import org.thoughtcrime.securesms.connect.DcEventCenter; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.mms.AttachmentManager; import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.util.Prefs; import org.thoughtcrime.securesms.util.ScreenLockUtil; import org.thoughtcrime.securesms.util.StorageUtil; import org.thoughtcrime.securesms.util.StreamUtil; @@ -121,9 +119,6 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment Preference manageKeys = this.findPreference("pref_manage_keys"); manageKeys.setOnPreferenceClickListener(new ManageKeysListener()); - Preference screenSecurity = this.findPreference(Prefs.SCREEN_SECURITY_PREF); - screenSecurity.setOnPreferenceChangeListener(new ScreenShotSecurityListener()); - Preference submitDebugLog = this.findPreference("pref_view_log"); submitDebugLog.setOnPreferenceClickListener(new ViewLogListener()); @@ -249,16 +244,6 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment } } - private class ScreenShotSecurityListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (Boolean) newValue; - Prefs.setScreenSecurityEnabled(getContext(), enabled); - Toast.makeText(getContext(), R.string.pref_screen_security_please_restart_hint, Toast.LENGTH_LONG).show(); - return true; - } - } - private class ViewLogListener implements Preference.OnPreferenceClickListener { @Override public boolean onPreferenceClick(Preference preference) { diff --git a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java index c1e104900..c7a256240 100644 --- a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java +++ b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java @@ -21,7 +21,6 @@ import androidx.preference.Preference; import com.b44t.messenger.DcContext; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; -import org.thoughtcrime.securesms.BlockedContactsActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.permissions.Permissions; @@ -35,12 +34,8 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { private ListPreference showEmails; private ListPreference mediaQuality; private ListPreference autoDownload; - private CheckBoxPreference readReceiptsCheckbox; private CheckBoxPreference subjectCheckbox; - private ListPreference autoDelDevice; - private ListPreference autoDelServer; - @Override public void onCreate(Bundle paramBundle) { super.onCreate(paramBundle); @@ -68,22 +63,11 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { return true; }); - readReceiptsCheckbox = (CheckBoxPreference) this.findPreference("pref_read_receipts"); - readReceiptsCheckbox.setOnPreferenceChangeListener(new ReadReceiptToggleListener()); - subjectCheckbox = (CheckBoxPreference) this.findPreference("pref_subject"); subjectCheckbox.setOnPreferenceChangeListener(new SubjectToggleListener()); - this.findPreference("preference_category_blocked").setOnPreferenceClickListener(new BlockedContactsClickListener()); - Preference backup = this.findPreference("pref_backup"); backup.setOnPreferenceClickListener(new BackupListener()); - - autoDelDevice = findPreference("autodel_device"); - autoDelDevice.setOnPreferenceChangeListener(new AutodelChangeListener("delete_device_after")); - - autoDelServer = findPreference("autodel_server"); - autoDelServer.setOnPreferenceChangeListener(new AutodelChangeListener("delete_server_after")); } @Override @@ -109,20 +93,7 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { autoDownload.setValue(value); updateListSummary(autoDownload, value); - readReceiptsCheckbox.setChecked(0 != dcContext.getConfigInt("mdns_enabled")); subjectCheckbox.setChecked(0 != dcContext.getConfigInt("subject_enabled")); - - initAutodelFromCore(); - } - - private void initAutodelFromCore() { - String value = Integer.toString(dcContext.getConfigInt("delete_server_after")); - autoDelServer.setValue(value); - updateListSummary(autoDelServer, value, value.equals("0")? null : getString(R.string.autodel_server_enabled_hint)); - - value = Integer.toString(dcContext.getConfigInt("delete_device_after")); - autoDelDevice.setValue(value); - updateListSummary(autoDelDevice, value); } // prefixes "Up to ..." to all entry names but the first one. @@ -167,11 +138,7 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { public static CharSequence getSummary(Context context) { DcContext dcContext = DcHelper.getContext(context); - final String onRes = context.getString(R.string.on); final String offRes = context.getString(R.string.off); - String readReceiptState = dcContext.getConfigInt("mdns_enabled")!=0? onRes : offRes; - boolean deleteOld = (dcContext.getConfigInt("delete_device_after")!=0 || dcContext.getConfigInt("delete_server_after")!=0); - String showEmails = "?"; switch (dcContext.getConfigInt("show_emails")) { case DcContext.DC_SHOW_EMAILS_OFF: showEmails = offRes; break; @@ -179,32 +146,10 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { case DcContext.DC_SHOW_EMAILS_ALL: showEmails = context.getString(R.string.pref_show_emails_all); break; } - String summary = context.getString(R.string.pref_show_emails) + " " + showEmails + ", " + - context.getString(R.string.pref_read_receipts) + " " + readReceiptState; - if (deleteOld) { - summary += ", " + context.getString(R.string.delete_old_messages) + " " + onRes; - } + String summary = context.getString(R.string.pref_show_emails) + " " + showEmails; return summary; } - private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(Preference preference) { - Intent intent = new Intent(getActivity(), BlockedContactsActivity.class); - startActivity(intent); - return true; - } - } - - private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (boolean) newValue; - dcContext.setConfigInt("mdns_enabled", enabled ? 1 : 0); - return true; - } - } - private class SubjectToggleListener implements Preference.OnPreferenceChangeListener { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -214,55 +159,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { } } - private class AutodelChangeListener implements Preference.OnPreferenceChangeListener { - private final String coreKey; - - AutodelChangeListener(String coreKey) { - this.coreKey = coreKey; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - int timeout = Util.objectToInt(newValue); - if (timeout>0) { - Context context = preference.getContext(); - boolean fromServer = coreKey.equals("delete_server_after"); - int delCount = DcHelper.getContext(context).estimateDeletionCount(fromServer, timeout); - - View gl = View.inflate(getActivity(), R.layout.dialog_with_checkbox, null); - CheckBox confirmCheckbox = gl.findViewById(R.id.dialog_checkbox); - TextView msg = gl.findViewById(R.id.dialog_message); - - // If we'd use both `setMessage()` and `setView()` on the same AlertDialog, on small screens the - // "OK" and "Cancel" buttons would not be show. So, put the message into our custom view: - msg.setText(String.format(context.getString(fromServer? - R.string.autodel_server_ask : R.string.autodel_device_ask), - delCount, getSelectedSummary(preference, newValue))); - confirmCheckbox.setText(R.string.autodel_confirm); - - new AlertDialog.Builder(context) - .setTitle(preference.getTitle()) - .setView(gl) - .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { - if (confirmCheckbox.isChecked()) { - dcContext.setConfigInt(coreKey, timeout); - initAutodelFromCore(); - } else { - onPreferenceChange(preference, newValue); - } - }) - .setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> initAutodelFromCore()) - .setCancelable(true) // Enable the user to quickly cancel if they are intimidated by the warnings :) - .setOnCancelListener(dialog -> initAutodelFromCore()) - .show(); - } else { - updateListSummary(preference, newValue); - dcContext.setConfigInt(coreKey, timeout); - } - return true; - } - } - /*********************************************************************************************** * Backup **********************************************************************************************/ diff --git a/src/org/thoughtcrime/securesms/preferences/PrivacyPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/PrivacyPreferenceFragment.java new file mode 100644 index 000000000..d8356ef0e --- /dev/null +++ b/src/org/thoughtcrime/securesms/preferences/PrivacyPreferenceFragment.java @@ -0,0 +1,170 @@ +package org.thoughtcrime.securesms.preferences; + +import static android.app.Activity.RESULT_OK; +import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SHOW_EMAILS; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.CheckBoxPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +import com.b44t.messenger.DcContext; + +import org.thoughtcrime.securesms.ApplicationPreferencesActivity; +import org.thoughtcrime.securesms.BlockedContactsActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.connect.DcHelper; +import org.thoughtcrime.securesms.util.Prefs; +import org.thoughtcrime.securesms.util.Util; + +public class PrivacyPreferenceFragment extends ListSummaryPreferenceFragment { + private static final String TAG = PrivacyPreferenceFragment.class.getSimpleName(); + + private CheckBoxPreference readReceiptsCheckbox; + + private ListPreference autoDelDevice; + private ListPreference autoDelServer; + + @Override + public void onCreate(Bundle paramBundle) { + super.onCreate(paramBundle); + + readReceiptsCheckbox = (CheckBoxPreference) this.findPreference("pref_read_receipts"); + readReceiptsCheckbox.setOnPreferenceChangeListener(new ReadReceiptToggleListener()); + + this.findPreference("preference_category_blocked").setOnPreferenceClickListener(new BlockedContactsClickListener()); + + autoDelDevice = findPreference("autodel_device"); + autoDelDevice.setOnPreferenceChangeListener(new AutodelChangeListener("delete_device_after")); + + autoDelServer = findPreference("autodel_server"); + autoDelServer.setOnPreferenceChangeListener(new AutodelChangeListener("delete_server_after")); + + Preference screenSecurity = this.findPreference(Prefs.SCREEN_SECURITY_PREF); + screenSecurity.setOnPreferenceChangeListener(new ScreenShotSecurityListener()); + } + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences_privacy); + } + + @Override + public void onResume() { + super.onResume(); + ((ApplicationPreferencesActivity)getActivity()).getSupportActionBar().setTitle(R.string.pref_privacy); + + readReceiptsCheckbox.setChecked(0 != dcContext.getConfigInt("mdns_enabled")); + initAutodelFromCore(); + } + + private void initAutodelFromCore() { + String value = Integer.toString(dcContext.getConfigInt("delete_server_after")); + autoDelServer.setValue(value); + updateListSummary(autoDelServer, value, value.equals("0")? null : getString(R.string.autodel_server_enabled_hint)); + + value = Integer.toString(dcContext.getConfigInt("delete_device_after")); + autoDelDevice.setValue(value); + updateListSummary(autoDelDevice, value); + } + + public static CharSequence getSummary(Context context) { + DcContext dcContext = DcHelper.getContext(context); + final String onRes = context.getString(R.string.on); + final String offRes = context.getString(R.string.off); + String readReceiptState = dcContext.getConfigInt("mdns_enabled")!=0? onRes : offRes; + boolean deleteOld = (dcContext.getConfigInt("delete_device_after")!=0 || dcContext.getConfigInt("delete_server_after")!=0); + + String summary = context.getString(R.string.pref_read_receipts) + " " + readReceiptState; + if (deleteOld) { + summary += ", " + context.getString(R.string.delete_old_messages) + " " + onRes; + } + return summary; + } + + private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent intent = new Intent(getActivity(), BlockedContactsActivity.class); + startActivity(intent); + return true; + } + } + + private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (boolean) newValue; + dcContext.setConfigInt("mdns_enabled", enabled ? 1 : 0); + return true; + } + } + + private class AutodelChangeListener implements Preference.OnPreferenceChangeListener { + private final String coreKey; + + AutodelChangeListener(String coreKey) { + this.coreKey = coreKey; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + int timeout = Util.objectToInt(newValue); + if (timeout>0) { + Context context = preference.getContext(); + boolean fromServer = coreKey.equals("delete_server_after"); + int delCount = DcHelper.getContext(context).estimateDeletionCount(fromServer, timeout); + + View gl = View.inflate(getActivity(), R.layout.dialog_with_checkbox, null); + CheckBox confirmCheckbox = gl.findViewById(R.id.dialog_checkbox); + TextView msg = gl.findViewById(R.id.dialog_message); + + // If we'd use both `setMessage()` and `setView()` on the same AlertDialog, on small screens the + // "OK" and "Cancel" buttons would not be show. So, put the message into our custom view: + msg.setText(String.format(context.getString(fromServer? + R.string.autodel_server_ask : R.string.autodel_device_ask), + delCount, getSelectedSummary(preference, newValue))); + confirmCheckbox.setText(R.string.autodel_confirm); + + new AlertDialog.Builder(context) + .setTitle(preference.getTitle()) + .setView(gl) + .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { + if (confirmCheckbox.isChecked()) { + dcContext.setConfigInt(coreKey, timeout); + initAutodelFromCore(); + } else { + onPreferenceChange(preference, newValue); + } + }) + .setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> initAutodelFromCore()) + .setCancelable(true) // Enable the user to quickly cancel if they are intimidated by the warnings :) + .setOnCancelListener(dialog -> initAutodelFromCore()) + .show(); + } else { + updateListSummary(preference, newValue); + dcContext.setConfigInt(coreKey, timeout); + } + return true; + } + } + + private class ScreenShotSecurityListener implements Preference.OnPreferenceChangeListener { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean enabled = (Boolean) newValue; + Prefs.setScreenSecurityEnabled(getContext(), enabled); + Toast.makeText(getContext(), R.string.pref_screen_security_please_restart_hint, Toast.LENGTH_LONG).show(); + return true; + } + } +}