mirror of
https://github.com/nextcloud/android.git
synced 2024-12-21 00:12:05 +03:00
Merge pull request #14061 from nextcloud/mdm-enforce-protection
Feature - MDM Enforce App Protection
This commit is contained in:
commit
72fdb340eb
10 changed files with 116 additions and 22 deletions
|
@ -119,6 +119,9 @@ object MDMConfig {
|
||||||
fun getPort(context: Context): Int =
|
fun getPort(context: Context): Int =
|
||||||
context.getRestriction(AppConfigKeys.ProxyPort, context.resources.getInteger(R.integer.proxy_port))
|
context.getRestriction(AppConfigKeys.ProxyPort, context.resources.getInteger(R.integer.proxy_port))
|
||||||
|
|
||||||
|
fun enforceProtection(context: Context): Boolean =
|
||||||
|
context.getRestriction(AppConfigKeys.EnforceProtection, context.resources.getBoolean(R.bool.enforce_protection))
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun <T : Any> Context.getRestriction(appConfigKey: AppConfigKeys, defaultValue: T): T {
|
private fun <T : Any> Context.getRestriction(appConfigKey: AppConfigKeys, defaultValue: T): T {
|
||||||
val restrictionsManager = getSystemService(Context.RESTRICTIONS_SERVICE) as? RestrictionsManager
|
val restrictionsManager = getSystemService(Context.RESTRICTIONS_SERVICE) as? RestrictionsManager
|
||||||
|
|
|
@ -17,11 +17,13 @@ import android.view.View
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import com.nextcloud.client.preferences.AppPreferences
|
import com.nextcloud.client.preferences.AppPreferences
|
||||||
|
import com.nextcloud.utils.mdm.MDMConfig
|
||||||
import com.owncloud.android.R
|
import com.owncloud.android.R
|
||||||
import com.owncloud.android.authentication.AuthenticatorActivity
|
import com.owncloud.android.authentication.AuthenticatorActivity
|
||||||
import com.owncloud.android.databinding.ActivitySplashBinding
|
import com.owncloud.android.databinding.ActivitySplashBinding
|
||||||
import com.owncloud.android.ui.activity.BaseActivity
|
import com.owncloud.android.ui.activity.BaseActivity
|
||||||
import com.owncloud.android.ui.activity.FileDisplayActivity
|
import com.owncloud.android.ui.activity.FileDisplayActivity
|
||||||
|
import com.owncloud.android.ui.activity.SettingsActivity
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LauncherActivity : BaseActivity() {
|
class LauncherActivity : BaseActivity() {
|
||||||
|
@ -65,7 +67,11 @@ class LauncherActivity : BaseActivity() {
|
||||||
private fun scheduleSplashScreen() {
|
private fun scheduleSplashScreen() {
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
if (user.isPresent) {
|
if (user.isPresent) {
|
||||||
startActivity(Intent(this, FileDisplayActivity::class.java))
|
if (MDMConfig.enforceProtection(this) && appPreferences.lockPreference == SettingsActivity.LOCK_NONE) {
|
||||||
|
startActivity(Intent(this, SettingsActivity::class.java))
|
||||||
|
} else {
|
||||||
|
startActivity(Intent(this, FileDisplayActivity::class.java))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startActivity(Intent(this, AuthenticatorActivity::class.java))
|
startActivity(Intent(this, AuthenticatorActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ import com.owncloud.android.services.OperationsService;
|
||||||
import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
|
import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
|
||||||
import com.owncloud.android.ui.NextcloudWebViewClient;
|
import com.owncloud.android.ui.NextcloudWebViewClient;
|
||||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||||
|
import com.owncloud.android.ui.activity.SettingsActivity;
|
||||||
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
|
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
|
||||||
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
|
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
|
||||||
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
|
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
|
||||||
|
@ -123,6 +124,7 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
@ -1361,11 +1363,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
|
||||||
|
|
||||||
private void endSuccess() {
|
private void endSuccess() {
|
||||||
if (!onlyAdd) {
|
if (!onlyAdd) {
|
||||||
Intent i = new Intent(this, FileDisplayActivity.class);
|
if (MDMConfig.INSTANCE.enforceProtection(this) && Objects.equals(preferences.getLockPreference(), SettingsActivity.LOCK_NONE)) {
|
||||||
i.setAction(FileDisplayActivity.RESTART);
|
Intent i = new Intent(this, SettingsActivity.class);
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
startActivity(i);
|
||||||
startActivity(i);
|
} else {
|
||||||
|
Intent i = new Intent(this, FileDisplayActivity.class);
|
||||||
|
i.setAction(FileDisplayActivity.RESTART);
|
||||||
|
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.preference.ListPreference
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import com.nextcloud.utils.extensions.setVisibleIf
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
class ListPreferenceDialog(context: Context?, attrs: AttributeSet?) : ListPreference(context, attrs) {
|
||||||
|
|
||||||
|
fun showDialog() {
|
||||||
|
if (!isDialogCreated()) {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissible(value: Boolean) {
|
||||||
|
if (isDialogCreated()) {
|
||||||
|
dialog.setCancelable(value)
|
||||||
|
dialog.setCanceledOnTouchOutside(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enableCancelButton(value: Boolean) {
|
||||||
|
if (isDialogCreated()) {
|
||||||
|
(dialog as? AlertDialog)?.let {
|
||||||
|
val cancelButton = it.getButton(Dialog.BUTTON_NEGATIVE)
|
||||||
|
cancelButton?.setVisibleIf(value)
|
||||||
|
cancelButton?.isEnabled = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isDialogCreated(): Boolean {
|
||||||
|
return dialog != null
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,7 @@ import com.owncloud.android.lib.common.ExternalLink;
|
||||||
import com.owncloud.android.lib.common.ExternalLinkType;
|
import com.owncloud.android.lib.common.ExternalLinkType;
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||||
import com.owncloud.android.providers.DocumentsStorageProvider;
|
import com.owncloud.android.providers.DocumentsStorageProvider;
|
||||||
|
import com.owncloud.android.ui.ListPreferenceDialog;
|
||||||
import com.owncloud.android.ui.ThemeableSwitchPreference;
|
import com.owncloud.android.ui.ThemeableSwitchPreference;
|
||||||
import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask;
|
import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask;
|
||||||
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
|
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
|
||||||
|
@ -75,6 +76,7 @@ import com.owncloud.android.utils.theme.ViewThemeUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -122,7 +124,7 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
|
|
||||||
private Uri serverBaseUri;
|
private Uri serverBaseUri;
|
||||||
|
|
||||||
private ListPreference lock;
|
private ListPreferenceDialog lock;
|
||||||
private ThemeableSwitchPreference showHiddenFiles;
|
private ThemeableSwitchPreference showHiddenFiles;
|
||||||
private ThemeableSwitchPreference showEcosystemApps;
|
private ThemeableSwitchPreference showEcosystemApps;
|
||||||
private AppCompatDelegate delegate;
|
private AppCompatDelegate delegate;
|
||||||
|
@ -139,7 +141,6 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
@Inject ViewThemeUtils viewThemeUtils;
|
@Inject ViewThemeUtils viewThemeUtils;
|
||||||
@Inject ConnectivityService connectivityService;
|
@Inject ConnectivityService connectivityService;
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -185,6 +186,15 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
|
|
||||||
// workaround for mismatched color when app dark mode and system dark mode don't agree
|
// workaround for mismatched color when app dark mode and system dark mode don't agree
|
||||||
setListBackground();
|
setListBackground();
|
||||||
|
showPasscodeDialogIfEnforceAppProtection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPasscodeDialogIfEnforceAppProtection() {
|
||||||
|
if (MDMConfig.INSTANCE.enforceProtection(this) && Objects.equals(preferences.getLockPreference(), SettingsActivity.LOCK_NONE) && lock != null) {
|
||||||
|
lock.showDialog();
|
||||||
|
lock.dismissible(false);
|
||||||
|
lock.enableCancelButton(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupDevCategory(PreferenceScreen preferenceScreen) {
|
private void setupDevCategory(PreferenceScreen preferenceScreen) {
|
||||||
|
@ -678,26 +688,35 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
private void setupLockPreference(PreferenceCategory preferenceCategoryDetails,
|
private void setupLockPreference(PreferenceCategory preferenceCategoryDetails,
|
||||||
boolean passCodeEnabled,
|
boolean passCodeEnabled,
|
||||||
boolean deviceCredentialsEnabled) {
|
boolean deviceCredentialsEnabled) {
|
||||||
lock = (ListPreference) findPreference(PREFERENCE_LOCK);
|
boolean enforceProtection = MDMConfig.INSTANCE.enforceProtection(this);
|
||||||
|
lock = (ListPreferenceDialog) findPreference(PREFERENCE_LOCK);
|
||||||
|
int optionSize = 3;
|
||||||
|
if (enforceProtection) {
|
||||||
|
optionSize = 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (lock != null && (passCodeEnabled || deviceCredentialsEnabled)) {
|
if (lock != null && (passCodeEnabled || deviceCredentialsEnabled)) {
|
||||||
ArrayList<String> lockEntries = new ArrayList<>(3);
|
ArrayList<String> lockEntries = new ArrayList<>(optionSize);
|
||||||
lockEntries.add(getString(R.string.prefs_lock_none));
|
|
||||||
lockEntries.add(getString(R.string.prefs_lock_using_passcode));
|
lockEntries.add(getString(R.string.prefs_lock_using_passcode));
|
||||||
lockEntries.add(getString(R.string.prefs_lock_using_device_credentials));
|
lockEntries.add(getString(R.string.prefs_lock_using_device_credentials));
|
||||||
|
|
||||||
ArrayList<String> lockValues = new ArrayList<>(3);
|
ArrayList<String> lockValues = new ArrayList<>(optionSize);
|
||||||
lockValues.add(LOCK_NONE);
|
|
||||||
lockValues.add(LOCK_PASSCODE);
|
lockValues.add(LOCK_PASSCODE);
|
||||||
lockValues.add(LOCK_DEVICE_CREDENTIALS);
|
lockValues.add(LOCK_DEVICE_CREDENTIALS);
|
||||||
|
|
||||||
if (!passCodeEnabled) {
|
if (!enforceProtection) {
|
||||||
lockEntries.remove(1);
|
lockEntries.add(getString(R.string.prefs_lock_none));
|
||||||
lockValues.remove(1);
|
lockValues.add(LOCK_NONE);
|
||||||
} else if (!deviceCredentialsEnabled ||
|
|
||||||
!DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) {
|
|
||||||
lockEntries.remove(2);
|
|
||||||
lockValues.remove(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!passCodeEnabled) {
|
||||||
|
lockEntries.remove(getString(R.string.prefs_lock_using_passcode));
|
||||||
|
lockValues.remove(LOCK_PASSCODE);
|
||||||
|
} else if (!deviceCredentialsEnabled || !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) {
|
||||||
|
lockEntries.remove(getString(R.string.prefs_lock_using_device_credentials));
|
||||||
|
lockValues.remove(LOCK_DEVICE_CREDENTIALS);
|
||||||
|
}
|
||||||
|
|
||||||
String[] lockEntriesArr = new String[lockEntries.size()];
|
String[] lockEntriesArr = new String[lockEntries.size()];
|
||||||
lockEntriesArr = lockEntries.toArray(lockEntriesArr);
|
lockEntriesArr = lockEntries.toArray(lockEntriesArr);
|
||||||
String[] lockValuesArr = new String[lockValues.size()];
|
String[] lockValuesArr = new String[lockValues.size()];
|
||||||
|
@ -706,6 +725,7 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
lock.setEntries(lockEntriesArr);
|
lock.setEntries(lockEntriesArr);
|
||||||
lock.setEntryValues(lockValuesArr);
|
lock.setEntryValues(lockValuesArr);
|
||||||
lock.setSummary(lock.getEntry());
|
lock.setSummary(lock.getEntry());
|
||||||
|
|
||||||
lock.setOnPreferenceChangeListener((preference, o) -> {
|
lock.setOnPreferenceChangeListener((preference, o) -> {
|
||||||
pendingLock = LOCK_NONE;
|
pendingLock = LOCK_NONE;
|
||||||
String oldValue = ((ListPreference) preference).getValue();
|
String oldValue = ((ListPreference) preference).getValue();
|
||||||
|
@ -935,7 +955,9 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
if (requestCode == ACTION_REQUEST_PASSCODE && resultCode == RESULT_OK) {
|
if (requestCode == ACTION_REQUEST_PASSCODE && resultCode == RESULT_CANCELED) {
|
||||||
|
showPasscodeDialogIfEnforceAppProtection();
|
||||||
|
} else if (requestCode == ACTION_REQUEST_PASSCODE && resultCode == RESULT_OK) {
|
||||||
String passcode = data.getStringExtra(PassCodeActivity.KEY_PASSCODE);
|
String passcode = data.getStringExtra(PassCodeActivity.KEY_PASSCODE);
|
||||||
if (passcode != null && passcode.length() == 4) {
|
if (passcode != null && passcode.length() == 4) {
|
||||||
SharedPreferences.Editor appPrefs = PreferenceManager
|
SharedPreferences.Editor appPrefs = PreferenceManager
|
||||||
|
|
|
@ -19,5 +19,6 @@ enum class AppConfigKeys(val key: String) {
|
||||||
DisableClipboard("disable_clipboard"),
|
DisableClipboard("disable_clipboard"),
|
||||||
DisableMoreExternalSite("disable_more_external_site"),
|
DisableMoreExternalSite("disable_more_external_site"),
|
||||||
DisableIntro("disable_intro"),
|
DisableIntro("disable_intro"),
|
||||||
DisableLog("disable_log")
|
DisableLog("disable_log"),
|
||||||
|
EnforceProtection("enforce_protection")
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
<bool name="disable_sharing">false</bool>
|
<bool name="disable_sharing">false</bool>
|
||||||
<bool name="disable_clipboard">false</bool>
|
<bool name="disable_clipboard">false</bool>
|
||||||
<bool name="disable_log">false</bool>
|
<bool name="disable_log">false</bool>
|
||||||
|
<bool name="enforce_protection">false</bool>
|
||||||
|
|
||||||
<!-- Flags to enable/disable some features -->
|
<!-- Flags to enable/disable some features -->
|
||||||
<string name="send_files_to_other_apps">on</string>
|
<string name="send_files_to_other_apps">on</string>
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<string name="app_config_base_url_title">Base URL</string>
|
<string name="app_config_base_url_title">Base URL</string>
|
||||||
<string name="app_config_proxy_host_title">Proxy Hostname</string>
|
<string name="app_config_proxy_host_title">Proxy Hostname</string>
|
||||||
<string name="app_config_proxy_port_title">Proxy Port</string>
|
<string name="app_config_proxy_port_title">Proxy Port</string>
|
||||||
|
<string name="app_config_enforce_protection_title">Enforce Protection</string>
|
||||||
<string name="offline_operations_file_does_not_exists_yet">File does not exists, yet. Please upload the file first.</string>
|
<string name="offline_operations_file_does_not_exists_yet">File does not exists, yet. Please upload the file first.</string>
|
||||||
<string name="offline_operations_worker_notification_delete_offline_folder">Delete Offline Folder</string>
|
<string name="offline_operations_worker_notification_delete_offline_folder">Delete Offline Folder</string>
|
||||||
<string name="offline_operations_worker_notification_conflict_text">Conflicted Folder: %s</string>
|
<string name="offline_operations_worker_notification_conflict_text">Conflicted Folder: %s</string>
|
||||||
|
|
|
@ -61,4 +61,10 @@
|
||||||
android:restrictionType="bool"
|
android:restrictionType="bool"
|
||||||
android:title="@string/app_config_disable_log_title" />
|
android:title="@string/app_config_disable_log_title" />
|
||||||
|
|
||||||
|
<restriction
|
||||||
|
android:key="enforce_protection"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:restrictionType="bool"
|
||||||
|
android:title="@string/app_config_enforce_protection_title" />
|
||||||
|
|
||||||
</restrictions>
|
</restrictions>
|
|
@ -35,7 +35,7 @@
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/prefs_category_details" android:key="details">
|
<PreferenceCategory android:title="@string/prefs_category_details" android:key="details">
|
||||||
<ListPreference
|
<com.owncloud.android.ui.ListPreferenceDialog
|
||||||
android:title="@string/prefs_lock"
|
android:title="@string/prefs_lock"
|
||||||
android:key="lock"
|
android:key="lock"
|
||||||
android:dialogTitle="@string/prefs_lock_title"
|
android:dialogTitle="@string/prefs_lock_title"
|
||||||
|
|
Loading…
Reference in a new issue