Skip to content

Commit 4d44cb3

Browse files
authored
Merge pull request #3642 from owncloud/feature/prevent_screenshots_dialogs
[FEATURE] Prevent taking screenshots in Dialogs (Android 12)
2 parents 46e9f20 + 261a6f4 commit 4d44cb3

17 files changed

Lines changed: 107 additions & 43 deletions
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* ownCloud Android client application
3+
*
4+
* @author David Crespo Ríos
5+
* Copyright (C) 2022 ownCloud GmbH.
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License version 2,
9+
* as published by the Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package com.owncloud.android.extensions
21+
22+
import android.app.Dialog
23+
import android.view.WindowManager
24+
import com.owncloud.android.BuildConfig
25+
import com.owncloud.android.R
26+
27+
fun Dialog.avoidScreenshotsIfNeeded() {
28+
if (!BuildConfig.DEBUG && context.resources?.getBoolean(R.bool.allow_screenshots) == false) {
29+
window?.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
30+
}
31+
}

owncloudApp/src/main/java/com/owncloud/android/extensions/FragmentExt.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ fun Fragment.showAlertDialog(
5454
.setPositiveButton(positiveButtonText, positiveButtonListener)
5555
.setNegativeButton(negativeButtonText, negativeButtonListener)
5656
.show()
57+
.avoidScreenshotsIfNeeded()
5758
}
5859

5960
fun Fragment.hideSoftKeyboard() {

owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/fragments/SettingsSecurityFragment.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ import androidx.preference.Preference
3333
import androidx.preference.PreferenceFragmentCompat
3434
import androidx.preference.PreferenceScreen
3535
import com.owncloud.android.R
36+
import com.owncloud.android.extensions.avoidScreenshotsIfNeeded
3637
import com.owncloud.android.extensions.showMessageInSnackbar
3738
import com.owncloud.android.presentation.ui.security.BiometricActivity
3839
import com.owncloud.android.presentation.ui.security.BiometricManager
3940
import com.owncloud.android.presentation.ui.security.LockTimeout
4041
import com.owncloud.android.presentation.ui.security.PREFERENCE_LOCK_TIMEOUT
41-
import com.owncloud.android.presentation.ui.security.passcode.PassCodeActivity
4242
import com.owncloud.android.presentation.ui.security.PatternActivity
43+
import com.owncloud.android.presentation.ui.security.passcode.PassCodeActivity
4344
import com.owncloud.android.presentation.ui.settings.fragments.SettingsFragment.Companion.removePreferenceFromScreen
4445
import com.owncloud.android.presentation.viewmodels.settings.SettingsSecurityViewModel
4546
import com.owncloud.android.utils.DocumentProviderUtils.Companion.notifyDocumentProviderRoots
@@ -219,6 +220,7 @@ class SettingsSecurityFragment : PreferenceFragmentCompat() {
219220
prefTouchesWithOtherVisibleWindows?.isChecked = true
220221
}
221222
.show()
223+
.avoidScreenshotsIfNeeded()
222224
}
223225
return@setOnPreferenceChangeListener false
224226
}

owncloudApp/src/main/java/com/owncloud/android/presentation/ui/sharing/fragments/PublicShareDialogFragment.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import com.owncloud.android.domain.capabilities.model.CapabilityBooleanType
4444
import com.owncloud.android.domain.capabilities.model.OCCapability
4545
import com.owncloud.android.domain.sharing.shares.model.OCShare
4646
import com.owncloud.android.domain.utils.Event.EventObserver
47+
import com.owncloud.android.extensions.avoidScreenshotsIfNeeded
4748
import com.owncloud.android.extensions.parseError
4849
import com.owncloud.android.lib.resources.shares.RemoteShare
4950
import com.owncloud.android.lib.resources.status.OwnCloudVersion
@@ -203,6 +204,8 @@ class PublicShareDialogFragment : DialogFragment() {
203204

204205
binding.saveButton.setOnClickListener { onSaveShareSetting() }
205206
binding.cancelButton.setOnClickListener { dismiss() }
207+
208+
dialog?.avoidScreenshotsIfNeeded()
206209
}
207210

208211
private fun initTitleAndLabels() {
@@ -448,8 +451,10 @@ class PublicShareDialogFragment : DialogFragment() {
448451
updateCapabilities(uiResult.data)
449452
listener?.dismissLoading()
450453
}
451-
is UIResult.Error -> {}
452-
is UIResult.Loading -> {}
454+
is UIResult.Error -> {
455+
}
456+
is UIResult.Loading -> {
457+
}
453458
}
454459
}
455460
}

owncloudApp/src/main/java/com/owncloud/android/ui/dialog/ConfirmationDialogFragment.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import androidx.appcompat.app.AlertDialog;
2828
import androidx.fragment.app.DialogFragment;
2929
import com.owncloud.android.R;
30+
import com.owncloud.android.extensions.DialogExtKt;
3031

3132
public class ConfirmationDialogFragment extends DialogFragment {
3233

@@ -45,13 +46,13 @@ public class ConfirmationDialogFragment extends DialogFragment {
4546
/**
4647
* Public factory method to create new ConfirmationDialogFragment instances.
4748
*
48-
* @param messageResId DataResult id for a message to show in the dialog.
49-
* @param messageArguments Arguments to complete the message, if it's a format string. May be null.
50-
* @param titleResId DataResult id for a text to show in the title.
51-
* 0 for default alert title, -1 for no title.
52-
* @param posBtn DataResult id for the text of the positive button. -1 for no positive button.
53-
* @param neuBtn DataResult id for the text of the neutral button. -1 for no neutral button.
54-
* @param negBtn DataResult id for the text of the negative button. -1 for no negative button.
49+
* @param messageResId DataResult id for a message to show in the dialog.
50+
* @param messageArguments Arguments to complete the message, if it's a format string. May be null.
51+
* @param titleResId DataResult id for a text to show in the title.
52+
* 0 for default alert title, -1 for no title.
53+
* @param posBtn DataResult id for the text of the positive button. -1 for no positive button.
54+
* @param neuBtn DataResult id for the text of the neutral button. -1 for no neutral button.
55+
* @param negBtn DataResult id for the text of the negative button. -1 for no negative button.
5556
* @return Dialog ready to show.
5657
*/
5758
public static ConfirmationDialogFragment newInstance(
@@ -134,7 +135,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
134135
dialog.dismiss();
135136
});
136137
}
137-
return builder.create();
138+
Dialog d = builder.create();
139+
DialogExtKt.avoidScreenshotsIfNeeded(d);
140+
return d;
138141
}
139142

140143
public interface ConfirmationDialogFragmentListener {

owncloudApp/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import androidx.fragment.app.Fragment;
3232
import androidx.fragment.app.FragmentTransaction;
3333
import com.owncloud.android.R;
34+
import com.owncloud.android.extensions.DialogExtKt;
3435

3536
/**
3637
* Dialog which will be displayed to user upon keep-in-sync file conflict.
@@ -57,7 +58,7 @@ public static ConflictsResolveDialog newInstance(String path, OnConflictDecision
5758

5859
@Override
5960
public Dialog onCreateDialog(Bundle savedInstanceState) {
60-
return new AlertDialog.Builder(getActivity())
61+
AlertDialog d = new AlertDialog.Builder(getActivity())
6162
.setIcon(R.drawable.ic_warning)
6263
.setTitle(R.string.conflict_title)
6364
.setMessage(getString(R.string.conflict_message))
@@ -80,6 +81,10 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
8081
}
8182
})
8283
.create();
84+
85+
DialogExtKt.avoidScreenshotsIfNeeded(d);
86+
87+
return d;
8388
}
8489

8590
public void showDialog(AppCompatActivity activity) {
@@ -93,15 +98,6 @@ public void showDialog(AppCompatActivity activity) {
9398
this.show(ft, "dialog");
9499
}
95100

96-
public void dismissDialog(AppCompatActivity activity) {
97-
Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(getTag());
98-
if (prev != null) {
99-
FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
100-
ft.remove(prev);
101-
ft.commit();
102-
}
103-
}
104-
105101
@Override
106102
public void onCancel(DialogInterface dialog) {
107103
mListener.conflictDecisionMade(Decision.CANCEL);

owncloudApp/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.google.android.material.snackbar.Snackbar;
3939
import com.owncloud.android.R;
4040
import com.owncloud.android.datamodel.OCFile;
41+
import com.owncloud.android.extensions.DialogExtKt;
4142
import com.owncloud.android.lib.resources.files.FileUtils;
4243
import com.owncloud.android.ui.activity.ComponentsGetter;
4344
import com.owncloud.android.utils.PreferenceUtils;
@@ -106,6 +107,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
106107
.setTitle(R.string.uploader_info_dirname);
107108
Dialog d = builder.create();
108109
d.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
110+
DialogExtKt.avoidScreenshotsIfNeeded(d);
109111
return d;
110112
}
111113

owncloudApp/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import androidx.fragment.app.DialogFragment;
3232
import com.owncloud.android.R;
3333
import com.owncloud.android.datamodel.OCFile;
34+
import com.owncloud.android.extensions.DialogExtKt;
3435

3536
import java.text.DateFormat;
3637
import java.text.SimpleDateFormat;
@@ -69,11 +70,11 @@ public class ExpirationDatePickerDialogFragment
6970
/**
7071
* Factory method to create new instances
7172
*
72-
* @param chosenDateInMillis Date chosen when the dialog appears, in milliseconds elapsed
73-
* since Jan 1, 1970. Needs to be after tomorrow, or tomorrow will be used
74-
* instead.
75-
* @param maxDateInMillis Maximum date selectable, in milliseconds elapsed since Jan 1, 1970.
76-
* Only will be set if greater or equals than chosenDateInMillis and tomorrow.
73+
* @param chosenDateInMillis Date chosen when the dialog appears, in milliseconds elapsed
74+
* since Jan 1, 1970. Needs to be after tomorrow, or tomorrow will be used
75+
* instead.
76+
* @param maxDateInMillis Maximum date selectable, in milliseconds elapsed since Jan 1, 1970.
77+
* Only will be set if greater or equals than chosenDateInMillis and tomorrow.
7778
* @return New dialog instance
7879
*/
7980
public static ExpirationDatePickerDialogFragment newInstance(long chosenDateInMillis,
@@ -146,16 +147,18 @@ public void onClick(DialogInterface dialog, int which) {
146147
// shown by default)
147148
picker.setCalendarViewShown(false);
148149

150+
DialogExtKt.avoidScreenshotsIfNeeded(dialog);
151+
149152
return dialog;
150153
}
151154

152155
/**
153156
* Called when the user choses an expiration date.
154157
*
155-
* @param view View instance where the date was chosen
156-
* @param year Year of the date chosen.
157-
* @param monthOfYear Month of the date chosen [0, 11]
158-
* @param dayOfMonth Day of the date chosen
158+
* @param view View instance where the date was chosen
159+
* @param year Year of the date chosen.
160+
* @param monthOfYear Month of the date chosen [0, 11]
161+
* @param dayOfMonth Day of the date chosen
159162
*/
160163
@Override
161164
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

owncloudApp/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import androidx.core.content.ContextCompat;
3535
import androidx.fragment.app.DialogFragment;
3636
import com.owncloud.android.R;
37+
import com.owncloud.android.extensions.DialogExtKt;
3738
import com.owncloud.android.utils.PreferenceUtils;
3839

3940
public class LoadingDialog extends DialogFragment {
@@ -51,8 +52,8 @@ public void onCreate(Bundle savedInstanceState) {
5152
/**
5253
* Public factory method to get dialog instances.
5354
*
54-
* @param messageId DataResult id for a message to show in the dialog.
55-
* @param cancelable If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
55+
* @param messageId DataResult id for a message to show in the dialog.
56+
* @param cancelable If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
5657
* @return New dialog instance, ready to show.
5758
*/
5859
public static LoadingDialog newInstance(int messageId, boolean cancelable) {
@@ -109,6 +110,7 @@ public boolean onKey(DialogInterface dialog, int keyCode,
109110
};
110111
dialog.setOnKeyListener(keyListener);
111112
}
113+
DialogExtKt.avoidScreenshotsIfNeeded(dialog);
112114
return dialog;
113115
}
114116

owncloudApp/src/main/java/com/owncloud/android/ui/dialog/RateMeDialog.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.owncloud.android.AppRater;
3737
import com.owncloud.android.R;
3838
import com.owncloud.android.extensions.ActivityExtKt;
39+
import com.owncloud.android.extensions.DialogExtKt;
3940
import com.owncloud.android.utils.PreferenceUtils;
4041
import timber.log.Timber;
4142

@@ -146,6 +147,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
146147
DialogInterface.OnKeyListener keyListener = (dialog, keyCode, event) -> keyCode == KeyEvent.KEYCODE_BACK;
147148
dialog.setOnKeyListener(keyListener);
148149
}
150+
DialogExtKt.avoidScreenshotsIfNeeded(dialog);
149151
return dialog;
150152
}
151153

0 commit comments

Comments
 (0)