From 9e6dfb644cbf0d1e06f77c6df98586d4f2b27b8f Mon Sep 17 00:00:00 2001 From: Hari Date: Thu, 12 Oct 2017 00:05:08 +0530 Subject: [PATCH 01/26] Add support for default credentials --- src/main/AndroidManifest.xml | 1 + .../authentication/PassCodeManager.java | 17 +++ .../android/ui/activity/Preferences.java | 51 ++++++++ .../activity/RequestCredentialsActivity.java | 65 ++++++++++ .../android/utils/DeviceCredentialUtils.java | 111 ++++++++++++++++++ src/main/res/values/setup.xml | 1 + src/main/res/values/strings.xml | 4 + src/main/res/xml/preferences.xml | 3 + 8 files changed, 253 insertions(+) create mode 100644 src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java create mode 100644 src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index a197673da8..5699696914 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -242,6 +242,7 @@ + diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index edded6b1c0..d5b86bab24 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -32,6 +32,8 @@ import com.owncloud.android.MainApp; import com.owncloud.android.ui.activity.FingerprintActivity; import com.owncloud.android.ui.activity.PassCodeActivity; import com.owncloud.android.ui.activity.Preferences; +import com.owncloud.android.ui.activity.RequestCredentialsActivity; +import com.owncloud.android.utils.DeviceCredentialUtils; import java.util.HashSet; import java.util.Set; @@ -44,6 +46,7 @@ public class PassCodeManager { exemptOfPasscodeActivities = new HashSet<>(); exemptOfPasscodeActivities.add(PassCodeActivity.class); exemptOfPasscodeActivities.add(FingerprintActivity.class); + sExemptOfPasscodeActivites.add(RequestCredentialsActivity.class); // other activities may be exempted, if needed } @@ -91,6 +94,14 @@ public class PassCodeManager { activity.startActivity(i); } + if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && Build.VERSION.SDK_INT >= + Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested() && + !DeviceCredentialUtils.tryEncrypt()) { + Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class); + i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + activity.startActivity(i); + } + visibleActivitiesCounter++; // keep it AFTER passCodeShouldBeRequested was checked } @@ -131,4 +142,10 @@ public class PassCodeManager { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false); } + + private boolean deviceCredentialsShouldBeRequested() { + SharedPreferences appPrefs = PreferenceManager + .getDefaultSharedPreferences(MainApp.getAppContext()); + return (appPrefs.getBoolean(Preferences.PREFERENCE_USE_DEVICE_CREDENTIALS, false)); + } } diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 593285780e..707b45796f 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -70,6 +70,7 @@ import com.owncloud.android.lib.common.ExternalLinkType; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.utils.DeviceCredentialUtils; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; @@ -87,10 +88,14 @@ public class Preferences extends PreferenceActivity private static final String TAG = Preferences.class.getSimpleName(); public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint"; + + public final static String PREFERENCE_USE_DEVICE_CREDENTIALS= "use_device_credentials"; + public static final String PREFERENCE_EXPERT_MODE = "expert_mode"; private static final int ACTION_REQUEST_PASSCODE = 5; private static final int ACTION_CONFIRM_PASSCODE = 6; + private static final int ACTION_CONFIRM_DEVICE_CREDENTIALS = 7; private static final int ACTION_REQUEST_CODE_DAVDROID_SETUP = 10; @@ -503,12 +508,15 @@ public class Preferences extends PreferenceActivity boolean fPassCodeEnabled = getResources().getBoolean(R.bool.passcode_enabled); boolean fPrintEnabled = getResources().getBoolean(R.bool.fingerprint_enabled); + boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials); boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); setupPasscodePreference(preferenceCategoryDetails, fPassCodeEnabled); setupFingerprintPreference(preferenceCategoryDetails, fPrintEnabled); + setupDeviceCredentialsPreference(preferenceCategoryDetails, fDeviceCredentialsEnabled); + setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); setupExpertModePreference(preferenceCategoryDetails); @@ -618,6 +626,39 @@ public class Preferences extends PreferenceActivity } } + private void setupDeviceCredentialsPreference(PreferenceCategory preferenceCategoryDetails, + boolean fDeviceCredentialsEnabled) { + SwitchPreference useDeviceCredentials = (SwitchPreference) findPreference(PREFERENCE_USE_DEVICE_CREDENTIALS); + if (useDeviceCredentials != null && fDeviceCredentialsEnabled && Build.VERSION.SDK_INT >= + Build.VERSION_CODES.M) { + if (!DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { + DisplayUtils.showSnackMessage(this, R.string.prefs_device_credentials_not_setup); + useDeviceCredentials.setChecked(false); + } else { + useDeviceCredentials.setOnPreferenceChangeListener( + new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + Boolean incoming = (Boolean) newValue; + if (incoming) { + SharedPreferences appPrefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + SharedPreferences.Editor editor = appPrefs.edit(); + editor.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, true).apply(); + return true; + } else { + Intent i = new Intent(getApplicationContext(), RequestCredentialsActivity.class); + startActivityForResult(i, ACTION_CONFIRM_DEVICE_CREDENTIALS); + return false; + } + } + }); + } + } else { + preferenceCategoryDetails.removePreference(useDeviceCredentials); + } + } + private void setupPasscodePreference(PreferenceCategory preferenceCategoryDetails, boolean fPassCodeEnabled) { pCode = (SwitchPreference) findPreference(PassCodeActivity.PREFERENCE_SET_PASSCODE); if (pCode != null && fPassCodeEnabled) { @@ -869,11 +910,21 @@ public class Preferences extends PreferenceActivity } } else if (requestCode == ACTION_REQUEST_CODE_DAVDROID_SETUP && resultCode == RESULT_OK) { DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_sync_setup_successful); + } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + data.getBooleanExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, false)) { + SharedPreferences.Editor appPrefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()).edit(); + appPrefs.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, false).apply(); + + DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); + } } } @NonNull @Override + @NonNull public MenuInflater getMenuInflater() { return getDelegate().getMenuInflater(); } diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java new file mode 100644 index 0000000000..6f26bdbf20 --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -0,0 +1,65 @@ +package com.owncloud.android.ui.activity; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.RequiresApi; +import android.widget.Toast; + +import com.owncloud.android.R; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.utils.AnalyticsUtils; +import com.owncloud.android.utils.DeviceCredentialUtils; + +/** + * Dummy activity that is used to handle the device's default authentication workflow. + */ +@RequiresApi(value = 23) +public class RequestCredentialsActivity extends Activity { + + private static final String TAG = RequestCredentialsActivity.class.getSimpleName(); + private static final String SCREEN_NAME = "Device credentials"; + + public final static String KEY_CHECK_RESULT = "KEY_CHECK_RESULT"; + private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { + if (resultCode == Activity.RESULT_OK && DeviceCredentialUtils.tryEncrypt()) { + finishWithResult(true); + } else { + Toast.makeText(this, R.string.default_credentials_wrong, Toast.LENGTH_SHORT).show(); + requestCredentials(); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + AnalyticsUtils.setCurrentScreenName(this, SCREEN_NAME, TAG); + DeviceCredentialUtils.createKey(); + requestCredentials(); + } + + private void requestCredentials() { + KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + if (keyguardManager != null) { + Intent i = keyguardManager.createConfirmDeviceCredentialIntent(null, null); + startActivityForResult(i, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); + } else { + Log_OC.e(TAG, "Keyguard manager is null"); + finishWithResult(false); + } + } + + private void finishWithResult(boolean success) { + Intent resultIntent = new Intent(); + resultIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + resultIntent.putExtra(KEY_CHECK_RESULT, success); + setResult(Activity.RESULT_OK, resultIntent); + finish(); + } +} diff --git a/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java b/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java new file mode 100644 index 0000000000..cf46b056fd --- /dev/null +++ b/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java @@ -0,0 +1,111 @@ +package com.owncloud.android.utils; + +import android.app.KeyguardManager; +import android.content.Context; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.support.annotation.RequiresApi; + +import com.owncloud.android.lib.common.utils.Log_OC; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + +/** + * Utility class with methods for handling device credentials. + */ +@RequiresApi(value = 23) +public class DeviceCredentialUtils { + + private static final String TAG = DeviceCredentialUtils.class.getSimpleName(); + + /** Alias for our key in the Android Key Store. */ + private static final String KEY_NAME = "Nextcloud"; + private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6}; + + private static final int AUTHENTICATION_DURATION_SECONDS = 30; + + private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; + + public static boolean areCredentialsAvailable(Context context) { + KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService( + Context.KEYGUARD_SERVICE); + if (keyguardManager != null) { + return keyguardManager.isKeyguardSecure(); + } else { + Log_OC.e(TAG, "Keyguard manager is null"); + return false; + } + } + + /** + * Creates a symmetric key in the Android Key Store which can only be used after the user has + * authenticated with device credentials within the last X seconds. + */ + public static void createKey() { + // Generate a key to decrypt payment credentials, tokens, etc. + try { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); + keyStore.load(null); + KeyGenerator keyGenerator = KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); + + // Set the alias of the entry in Android KeyStore where the key will appear + // and the constrains (purposes) in the constructor of the Builder + keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_CBC) + .setUserAuthenticationRequired(true) + // Require that the user has unlocked in the last 30 seconds + .setUserAuthenticationValidityDurationSeconds(AUTHENTICATION_DURATION_SECONDS) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) + .build()); + keyGenerator.generateKey(); + } catch (NoSuchAlgorithmException | NoSuchProviderException + | InvalidAlgorithmParameterException | KeyStoreException + | CertificateException | IOException e) { + Log_OC.e(TAG, "Exception: " + e.getMessage()); + } + } + + /** + * Tries to encrypt some data with the generated key in {@link #createKey} which + * only works if the user has just authenticated via device credentials. + */ + public static boolean tryEncrypt() { + try { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); + keyStore.load(null); + SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null); + Cipher cipher = Cipher.getInstance( + KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + + KeyProperties.ENCRYPTION_PADDING_PKCS7); + + // Try encrypting something, it will only work if the user authenticated within + // the last AUTHENTICATION_DURATION_SECONDS seconds. + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + cipher.doFinal(SECRET_BYTE_ARRAY); + + // If the user has recently authenticated, you will reach here. + return true; + } catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException | + CertificateException | UnrecoverableKeyException | IOException + | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { + return false; + } + } +} diff --git a/src/main/res/values/setup.xml b/src/main/res/values/setup.xml index a72094411c..8becf4a426 100644 --- a/src/main/res/values/setup.xml +++ b/src/main/res/values/setup.xml @@ -91,6 +91,7 @@ true true + true true true true diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 80e67ae350..68ec956347 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -42,6 +42,8 @@ Passcode lock Fingerprint lock No fingerprints have been set up. + Lock using device credentials + No device credentials have been set up. Expert mode Show hidden files Delete history @@ -200,6 +202,8 @@ Remote: %1$s Insufficient space prevents copying the selected files into the %1$s folder. Would you like to move them there instead? Please enter your passcode + Incorrect credentials + Credentials disabled Enter your passcode The passcode will be requested every time the app is started diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index f4a3c19bb6..8c6c16d96b 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -45,6 +45,9 @@ + From a832abd87ef999b493aba27b219c8ebe93a4b7a2 Mon Sep 17 00:00:00 2001 From: Hari Date: Sun, 5 Nov 2017 20:35:07 +0530 Subject: [PATCH 02/26] Remove fingerprint authentication --- src/main/AndroidManifest.xml | 1 - .../authentication/PassCodeManager.java | 28 +- .../ui/activity/FingerprintActivity.java | 341 ------------------ .../android/ui/activity/Preferences.java | 73 +--- src/main/res/layout/fingerprintlock.xml | 65 ---- src/main/res/values/setup.xml | 3 +- src/main/res/values/strings.xml | 5 - src/main/res/xml/preferences.xml | 3 - 8 files changed, 21 insertions(+), 498 deletions(-) delete mode 100644 src/main/java/com/owncloud/android/ui/activity/FingerprintActivity.java delete mode 100644 src/main/res/layout/fingerprintlock.xml diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 5699696914..c5dead01fc 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -241,7 +241,6 @@ - diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index d5b86bab24..9f6b2529b9 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -29,7 +29,6 @@ import android.preference.PreferenceManager; import android.view.WindowManager; import com.owncloud.android.MainApp; -import com.owncloud.android.ui.activity.FingerprintActivity; import com.owncloud.android.ui.activity.PassCodeActivity; import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.ui.activity.RequestCredentialsActivity; @@ -43,10 +42,9 @@ public class PassCodeManager { private static final Set exemptOfPasscodeActivities; static { - exemptOfPasscodeActivities = new HashSet<>(); + exemptOfPasscodeActivities = new HashSet(); exemptOfPasscodeActivities.add(PassCodeActivity.class); - exemptOfPasscodeActivities.add(FingerprintActivity.class); - sExemptOfPasscodeActivites.add(RequestCredentialsActivity.class); + exemptOfPasscodeActivities.add(RequestCredentialsActivity.class); // other activities may be exempted, if needed } @@ -68,7 +66,7 @@ public class PassCodeManager { protected PassCodeManager() {} public void onActivityCreated(Activity activity) { - if (passCodeIsEnabled() || fingerprintIsEnabled()) { + if (passCodeIsEnabled() || deviceCredentialsAreEnabled()) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } else { activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); @@ -84,15 +82,6 @@ public class PassCodeManager { activity.startActivity(i); } - if (!exemptOfPasscodeActivities.contains(activity.getClass()) && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - fingerprintShouldBeRequested() && FingerprintActivity.isFingerprintReady(MainApp.getAppContext())) { - - Intent i = new Intent(MainApp.getAppContext(), FingerprintActivity.class); - i.setAction(PassCodeActivity.ACTION_CHECK); - i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - activity.startActivity(i); - } if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested() && @@ -111,7 +100,7 @@ public class PassCodeManager { } setUnlockTimestamp(); PowerManager powerMgr = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); - if ((passCodeIsEnabled() || fingerprintIsEnabled())&& powerMgr != null && !powerMgr.isScreenOn()) { + if ((passCodeIsEnabled() || deviceCredentialsAreEnabled())&& powerMgr != null && !powerMgr.isScreenOn()) { activity.moveTaskToBack(true); } } @@ -129,8 +118,11 @@ public class PassCodeManager { return (appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false)); } - private boolean fingerprintShouldBeRequested() { - return (fingerprintIsEnabled() && hasAuthenticationTimeoutExpired()); + private boolean deviceCredentialsShouldBeRequested() { + if ((System.currentTimeMillis() - mTimestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { + return deviceCredentialsAreEnabled(); + } + return false; } private boolean hasAuthenticationTimeoutExpired() { @@ -143,7 +135,7 @@ public class PassCodeManager { appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false); } - private boolean deviceCredentialsShouldBeRequested() { + private boolean deviceCredentialsAreEnabled() { SharedPreferences appPrefs = PreferenceManager .getDefaultSharedPreferences(MainApp.getAppContext()); return (appPrefs.getBoolean(Preferences.PREFERENCE_USE_DEVICE_CREDENTIALS, false)); diff --git a/src/main/java/com/owncloud/android/ui/activity/FingerprintActivity.java b/src/main/java/com/owncloud/android/ui/activity/FingerprintActivity.java deleted file mode 100644 index 271b086363..0000000000 --- a/src/main/java/com/owncloud/android/ui/activity/FingerprintActivity.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Florian Lentz - * Copyright (C) 2017 Florian Lentz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or 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 AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this program. If not, see . - */ -package com.owncloud.android.ui.activity; - -import android.Manifest; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; -import android.os.Bundle; -import android.os.CancellationSignal; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyPermanentlyInvalidatedException; -import android.security.keystore.KeyProperties; -import android.support.annotation.RequiresApi; -import android.support.v4.app.ActivityCompat; -import android.support.v4.graphics.drawable.DrawableCompat; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.KeyEvent; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.utils.ThemeUtils; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; - -/** - * Activity to handle access to the app based on the fingerprint. - */ -@RequiresApi(Build.VERSION_CODES.M) -public class FingerprintActivity extends AppCompatActivity { - - private static final String TAG = FingerprintActivity.class.getSimpleName(); - - public final static String KEY_CHECK_RESULT = "KEY_CHECK_RESULT"; - - public static final String ANDROID_KEY_STORE = "AndroidKeyStore"; - - private KeyStore keyStore; - // Variable used for storing the key in the Android Keystore container - private static final String KEY_NAME = "Nextcloud"; - private Cipher cipher; - - private CancellationSignal cancellationSignal; - - /** - * Initializes the activity. - * - * @param savedInstanceState Previously saved state - irrelevant in this case - */ - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.fingerprintlock); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().setStatusBarColor(ThemeUtils.primaryDarkColor(this)); - } - - Toolbar toolbar = findViewById(R.id.toolbar); - toolbar.setTitleTextColor(ThemeUtils.fontColor(this)); - toolbar.setBackground(new ColorDrawable(ThemeUtils.primaryColor(this, false))); - } - - private void startFingerprint() { - TextView fingerprintTextView = findViewById(R.id.scanfingerprinttext); - - FingerprintManager fingerprintManager = - (FingerprintManager) MainApp.getAppContext().getSystemService(Context.FINGERPRINT_SERVICE); - - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) - != PackageManager.PERMISSION_GRANTED) { - return; - } - KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); - - if (keyguardManager.isKeyguardSecure()) { - generateKey(); - - if (cipherInit()) { - FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher); - FingerprintHandler.Callback callback = new FingerprintHandler.Callback() { - @Override - public void onAuthenticated() { - fingerprintResult(true); - } - - @Override - public void onFailed(String error) { - Toast.makeText(MainApp.getAppContext(), error, Toast.LENGTH_LONG).show(); - ImageView imageView = findViewById(R.id.fingerprinticon); - int[][] states = new int[][]{ - new int[]{android.R.attr.state_activated}, - new int[]{-android.R.attr.state_activated} - }; - int[] colors = new int[]{Color.parseColor("#FF0000"), Color.RED}; - ColorStateList csl = new ColorStateList(states, colors); - Drawable drawable = DrawableCompat.wrap(imageView.getDrawable()); - DrawableCompat.setTintList(drawable, csl); - imageView.setImageDrawable(drawable); - } - }; - - FingerprintHandler helper = new FingerprintHandler(fingerprintTextView, callback); - cancellationSignal = new CancellationSignal(); - if (ActivityCompat.checkSelfPermission(MainApp.getAppContext(), Manifest.permission.USE_FINGERPRINT) - != PackageManager.PERMISSION_GRANTED) { - return; - } - fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, helper, null); - } - } - } - - @Override - public void onResume(){ - super.onResume(); - startFingerprint(); - ImageView imageView = findViewById(R.id.fingerprinticon); - imageView.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_fingerprint, ThemeUtils.primaryColor(this))); - } - - @Override - public void onStop(){ - super.onStop(); - if(cancellationSignal != null) { - cancellationSignal.cancel(); - } - } - - /** - * Overrides click on the BACK arrow to prevent fingerprint from being worked around. - * - * @param keyCode Key code of the key that triggered the down event. - * @param event Event triggered. - * @return 'True' when the key event was processed by this method. - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0 || super.onKeyDown(keyCode, event); - } - - protected void generateKey() { - try { - keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); - } catch (Exception e) { - Log_OC.e(TAG, "Error getting KeyStore", e); - } - - KeyGenerator keyGenerator; - try { - keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); - - keyStore.load(null); - keyGenerator.init( - new KeyGenParameterSpec.Builder( - KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT - ) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setUserAuthenticationRequired(true) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .build()); - keyGenerator.generateKey(); - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertificateException | IOException | - NoSuchProviderException e) { - Log_OC.e(TAG, "Exception: " + e.getMessage()); - } - } - - public boolean cipherInit() { - try { - cipher = Cipher.getInstance( - KeyProperties.KEY_ALGORITHM_AES + "/" - + KeyProperties.BLOCK_MODE_CBC + "/" - + KeyProperties.ENCRYPTION_PADDING_PKCS7); - } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { - return false; - } - - try { - keyStore.load(null); - SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME, null); - cipher.init(Cipher.ENCRYPT_MODE, key); - return true; - } catch (KeyPermanentlyInvalidatedException e) { - return false; - } catch (KeyStoreException - | CertificateException - | UnrecoverableKeyException - | IOException - | NoSuchAlgorithmException - | InvalidKeyException e) { - return false; - } - } - - private void fingerprintResult(boolean fingerOk) { - if (fingerOk) { - Intent resultIntent = new Intent(); - resultIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - resultIntent.putExtra(KEY_CHECK_RESULT, true); - setResult(RESULT_OK, resultIntent); - finish(); - } else { - showErrorAndRestart(R.string.fingerprint_unknown); - } - } - - private void showErrorAndRestart(int errorMessage) { - CharSequence errorSeq = getString(errorMessage); - Toast.makeText(this, errorSeq, Toast.LENGTH_LONG).show(); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - static public boolean isFingerprintCapable(Context context) { - try { - FingerprintManager fingerprintManager = - (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); - - if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) - != PackageManager.PERMISSION_GRANTED) { - return false; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return fingerprintManager.isHardwareDetected(); - } - } catch (Exception e) { - return false; - } - return false; - } - - static public boolean isFingerprintReady(Context context) { - try { - FingerprintManager fingerprintManager = - (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); - - return ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) == - PackageManager.PERMISSION_GRANTED && fingerprintManager.isHardwareDetected() && - fingerprintManager.hasEnrolledFingerprints(); - } catch (Exception e) { - return false; - } - } -} - -@RequiresApi(api = Build.VERSION_CODES.M) -class FingerprintHandler extends FingerprintManager.AuthenticationCallback { - - private TextView text; - private Callback callback; - - // Constructor - FingerprintHandler(TextView mText, Callback mCallback) { - text = mText; - callback = mCallback; - } - - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) { - // this.update(String.valueOf(errString), false); - } - - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - this.update(String.valueOf(helpString), false); - } - - @Override - public void onAuthenticationFailed() { - this.update(MainApp.getAppContext().getString(R.string.fingerprint_unknown), false); - } - - @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { - this.update("Fingerprint Authentication succeeded.", true); - } - - public void update(final String e, Boolean success) { - if(success) { - text.postDelayed(new Runnable() { - @Override - public void run() { - callback.onAuthenticated(); - } - }, 0); - } else { - text.postDelayed(new Runnable() { - @Override - public void run() { - callback.onFailed(e); - } - }, 0); - } - } - - interface Callback { - void onAuthenticated(); - void onFailed(String error); - } -} \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 707b45796f..18ec12d9b2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -87,8 +87,6 @@ public class Preferences extends PreferenceActivity private static final String TAG = Preferences.class.getSimpleName(); - public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint"; - public final static String PREFERENCE_USE_DEVICE_CREDENTIALS= "use_device_credentials"; public static final String PREFERENCE_EXPERT_MODE = "expert_mode"; @@ -109,7 +107,6 @@ public class Preferences extends PreferenceActivity private Uri mUri; private SwitchPreference pCode; - private SwitchPreference fPrint; private SwitchPreference mShowHiddenFiles; private SwitchPreference mExpertMode; private AppCompatDelegate mDelegate; @@ -372,6 +369,7 @@ public class Preferences extends PreferenceActivity if (pFeedback != null) { if (feedbackEnabled) { pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override public boolean onPreferenceClick(Preference preference) { String feedbackMail = getString(R.string.mail_feedback); @@ -507,29 +505,28 @@ public class Preferences extends PreferenceActivity accentColor)); boolean fPassCodeEnabled = getResources().getBoolean(R.bool.passcode_enabled); - boolean fPrintEnabled = getResources().getBoolean(R.bool.fingerprint_enabled); boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials); boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); + boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light); setupPasscodePreference(preferenceCategoryDetails, fPassCodeEnabled); - setupFingerprintPreference(preferenceCategoryDetails, fPrintEnabled); - setupDeviceCredentialsPreference(preferenceCategoryDetails, fDeviceCredentialsEnabled); setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); - setupExpertModePreference(preferenceCategoryDetails); + setupExpertModePreference(preferenceCategoryDetails, fSyncedFolderLightEnabled); - if (!fShowHiddenFilesEnabled && !fPrintEnabled && !fPassCodeEnabled) { + if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && !fShowHiddenFilesEnabled && fSyncedFolderLightEnabled) { preferenceScreen.removePreference(preferenceCategoryDetails); } } - private void setupExpertModePreference(PreferenceCategory preferenceCategoryDetails) { + private void setupExpertModePreference(PreferenceCategory preferenceCategoryDetails, + boolean fSyncedFolderLightEnabled) { mExpertMode = (SwitchPreference) findPreference(PREFERENCE_EXPERT_MODE); - if (getResources().getBoolean(R.bool.syncedFolder_light)) { + if (fSyncedFolderLightEnabled) { preferenceCategoryDetails.removePreference(mExpertMode); } else { mExpertMode = (SwitchPreference) findPreference(PREFERENCE_EXPERT_MODE); @@ -577,66 +574,16 @@ public class Preferences extends PreferenceActivity } } - private void setupFingerprintPreference(PreferenceCategory preferenceCategoryDetails, boolean fPrintEnabled) { - fPrint = (SwitchPreference) findPreference(PREFERENCE_USE_FINGERPRINT); - if (fPrint != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (FingerprintActivity.isFingerprintCapable(MainApp.getAppContext()) && fPrintEnabled) { - final Activity activity = this; - fPrint.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - Boolean incoming = (Boolean) newValue; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (FingerprintActivity.isFingerprintReady(MainApp.getAppContext())) { - SharedPreferences appPrefs = - PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - SharedPreferences.Editor editor = appPrefs.edit(); - editor.putBoolean(PREFERENCE_USE_FINGERPRINT, incoming); - editor.apply(); - return true; - } else { - if (incoming) { - DisplayUtils.showSnackMessage(activity, R.string.prefs_fingerprint_notsetup); - fPrint.setChecked(false); - } - SharedPreferences appPrefs = - PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - SharedPreferences.Editor editor = appPrefs.edit(); - editor.putBoolean(PREFERENCE_USE_FINGERPRINT, false); - editor.apply(); - return false; - } - } else { - return false; - } - } - }); - if (!FingerprintActivity.isFingerprintReady(MainApp.getAppContext())) { - fPrint.setChecked(false); - } - - } else { - preferenceCategoryDetails.removePreference(fPrint); - } - } else { - preferenceCategoryDetails.removePreference(fPrint); - } - } - } - private void setupDeviceCredentialsPreference(PreferenceCategory preferenceCategoryDetails, - boolean fDeviceCredentialsEnabled) { + boolean deviceCredentialsEnabled) { SwitchPreference useDeviceCredentials = (SwitchPreference) findPreference(PREFERENCE_USE_DEVICE_CREDENTIALS); - if (useDeviceCredentials != null && fDeviceCredentialsEnabled && Build.VERSION.SDK_INT >= + if (useDeviceCredentials != null && deviceCredentialsEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { DisplayUtils.showSnackMessage(this, R.string.prefs_device_credentials_not_setup); useDeviceCredentials.setChecked(false); } else { - useDeviceCredentials.setOnPreferenceChangeListener( - new OnPreferenceChangeListener() { + useDeviceCredentials.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { Boolean incoming = (Boolean) newValue; diff --git a/src/main/res/layout/fingerprintlock.xml b/src/main/res/layout/fingerprintlock.xml deleted file mode 100644 index f2f57192de..0000000000 --- a/src/main/res/layout/fingerprintlock.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/main/res/values/setup.xml b/src/main/res/values/setup.xml index 8becf4a426..0bbd029431 100644 --- a/src/main/res/values/setup.xml +++ b/src/main/res/values/setup.xml @@ -89,9 +89,8 @@ false - true true - true + true true true true diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 68ec956347..ca7d734400 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -40,8 +40,6 @@ More Manage accounts Passcode lock - Fingerprint lock - No fingerprints have been set up. Lock using device credentials No device credentials have been set up. Expert mode @@ -619,9 +617,6 @@ Skip - Please scan your finger - Finger not recognized - E-mail Phone number diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 8c6c16d96b..e61ec04c97 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -42,9 +42,6 @@ - From 62ceb226f6e5cf461d0ff46bf1912291810a2ed4 Mon Sep 17 00:00:00 2001 From: Hari Date: Mon, 6 Nov 2017 00:14:25 +0530 Subject: [PATCH 03/26] Fix codestyle and follow suggestions --- .../authentication/PassCodeManager.java | 4 +- .../android/ui/activity/Preferences.java | 15 +++--- .../activity/RequestCredentialsActivity.java | 28 +++++++++-- .../android/utils/DeviceCredentialUtils.java | 47 +++++++++++++++---- src/main/res/values/setup.xml | 9 ++++ 5 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index 9f6b2529b9..5fd83f2e01 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -85,7 +85,7 @@ public class PassCodeManager { if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested() && - !DeviceCredentialUtils.tryEncrypt()) { + !DeviceCredentialUtils.tryEncrypt(activity)) { Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); activity.startActivity(i); @@ -138,6 +138,6 @@ public class PassCodeManager { private boolean deviceCredentialsAreEnabled() { SharedPreferences appPrefs = PreferenceManager .getDefaultSharedPreferences(MainApp.getAppContext()); - return (appPrefs.getBoolean(Preferences.PREFERENCE_USE_DEVICE_CREDENTIALS, false)); + return appPrefs.getBoolean(Preferences.PREFERENCE_USE_DEVICE_CREDENTIALS, false); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 18ec12d9b2..b4b0d20636 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -857,15 +857,14 @@ public class Preferences extends PreferenceActivity } } else if (requestCode == ACTION_REQUEST_CODE_DAVDROID_SETUP && resultCode == RESULT_OK) { DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_sync_setup_successful); - } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - data.getBooleanExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, false)) { - SharedPreferences.Editor appPrefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); - appPrefs.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, false).apply(); + } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + data.getBooleanExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, false)) { + SharedPreferences.Editor appPrefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()).edit(); + appPrefs.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, false).apply(); - DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); - } + DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index 6f26bdbf20..94894980c2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -1,9 +1,30 @@ +/* + * Nextcloud Android client application + * + * @author Harikrishnan Rajan + * Copyright (C) 2017 + * Copyright (C) 2017 Nextcloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License version 3, + * as published by the Free Software Foundation. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ package com.owncloud.android.ui.activity; import android.app.Activity; import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.support.annotation.RequiresApi; import android.widget.Toast; @@ -15,7 +36,7 @@ import com.owncloud.android.utils.DeviceCredentialUtils; /** * Dummy activity that is used to handle the device's default authentication workflow. */ -@RequiresApi(value = 23) +@RequiresApi(Build.VERSION_CODES.M) public class RequestCredentialsActivity extends Activity { private static final String TAG = RequestCredentialsActivity.class.getSimpleName(); @@ -27,7 +48,8 @@ public class RequestCredentialsActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { - if (resultCode == Activity.RESULT_OK && DeviceCredentialUtils.tryEncrypt()) { + if (resultCode == Activity.RESULT_OK && DeviceCredentialUtils + .tryEncrypt(getApplicationContext())) { finishWithResult(true); } else { Toast.makeText(this, R.string.default_credentials_wrong, Toast.LENGTH_SHORT).show(); @@ -40,7 +62,7 @@ public class RequestCredentialsActivity extends Activity { protected void onResume() { super.onResume(); AnalyticsUtils.setCurrentScreenName(this, SCREEN_NAME, TAG); - DeviceCredentialUtils.createKey(); + DeviceCredentialUtils.createKey(getApplicationContext()); requestCredentials(); } diff --git a/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java b/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java index cf46b056fd..e8def67369 100644 --- a/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java +++ b/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java @@ -1,14 +1,38 @@ +/* + * Nextcloud Android client application + * + * @author Harikrishnan Rajan + * Copyright (C) 2017 + * Copyright (C) 2017 Nextcloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License version 3, + * as published by the Free Software Foundation. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ package com.owncloud.android.utils; import android.app.KeyguardManager; import android.content.Context; +import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.support.annotation.RequiresApi; +import com.owncloud.android.R; import com.owncloud.android.lib.common.utils.Log_OC; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyStore; @@ -28,15 +52,11 @@ import javax.crypto.SecretKey; /** * Utility class with methods for handling device credentials. */ -@RequiresApi(value = 23) +@RequiresApi(Build.VERSION_CODES.M) public class DeviceCredentialUtils { private static final String TAG = DeviceCredentialUtils.class.getSimpleName(); - /** Alias for our key in the Android Key Store. */ - private static final String KEY_NAME = "Nextcloud"; - private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6}; - private static final int AUTHENTICATION_DURATION_SECONDS = 30; private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; @@ -56,8 +76,9 @@ public class DeviceCredentialUtils { * Creates a symmetric key in the Android Key Store which can only be used after the user has * authenticated with device credentials within the last X seconds. */ - public static void createKey() { + public static void createKey(Context context) { // Generate a key to decrypt payment credentials, tokens, etc. + final String keyName = context.getResources().getString(R.string.secret_key_name); try { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); @@ -66,7 +87,7 @@ public class DeviceCredentialUtils { // Set the alias of the entry in Android KeyStore where the key will appear // and the constrains (purposes) in the constructor of the Builder - keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME, + keyGenerator.init(new KeyGenParameterSpec.Builder(keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) @@ -86,11 +107,17 @@ public class DeviceCredentialUtils { * Tries to encrypt some data with the generated key in {@link #createKey} which * only works if the user has just authenticated via device credentials. */ - public static boolean tryEncrypt() { + public static boolean tryEncrypt(Context context) { try { + final String keyName = context.getResources().getString(R.string.secret_key_name); + final int[] secretIntArray = context.getResources().getIntArray(R.array.secret_byte_array); + ByteBuffer byteBuffer = ByteBuffer.allocate(secretIntArray.length * 4); + IntBuffer intBuffer = byteBuffer.asIntBuffer(); + intBuffer.put(secretIntArray); + byte[] secretByteArray = byteBuffer.array(); KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); - SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null); + SecretKey secretKey = (SecretKey) keyStore.getKey(keyName, null); Cipher cipher = Cipher.getInstance( KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); @@ -98,7 +125,7 @@ public class DeviceCredentialUtils { // Try encrypting something, it will only work if the user authenticated within // the last AUTHENTICATION_DURATION_SECONDS seconds. cipher.init(Cipher.ENCRYPT_MODE, secretKey); - cipher.doFinal(SECRET_BYTE_ARRAY); + cipher.doFinal(secretByteArray); // If the user has recently authenticated, you will reach here. return true; diff --git a/src/main/res/values/setup.xml b/src/main/res/values/setup.xml index 0bbd029431..0161e6c747 100644 --- a/src/main/res/values/setup.xml +++ b/src/main/res/values/setup.xml @@ -91,6 +91,15 @@ true true + Nextcloud + + 1 + 2 + 3 + 4 + 5 + 6 + true true true From 4cd916848ba80c9c44ac0cce93387143e2b03b77 Mon Sep 17 00:00:00 2001 From: Hari Date: Mon, 6 Nov 2017 12:47:50 +0530 Subject: [PATCH 04/26] Disable lock after user confirms credentials --- .../java/com/owncloud/android/ui/activity/Preferences.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index b4b0d20636..9fd245fdc8 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -863,7 +863,9 @@ public class Preferences extends PreferenceActivity SharedPreferences.Editor appPrefs = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()).edit(); appPrefs.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, false).apply(); - + SwitchPreference useDeviceCredentials = (SwitchPreference) + findPreference(PREFERENCE_USE_DEVICE_CREDENTIALS); + useDeviceCredentials.setChecked(false); DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); } } From 6625d4f3b7c2599c2eddac120a401f4419c6d9a6 Mon Sep 17 00:00:00 2001 From: Hari Date: Mon, 6 Nov 2017 13:26:26 +0530 Subject: [PATCH 05/26] Fix crash when no credentials are available --- .../android/ui/activity/Preferences.java | 43 ++++++++++--------- .../activity/RequestCredentialsActivity.java | 11 ++++- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 9fd245fdc8..516ab64fd9 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -505,7 +505,7 @@ public class Preferences extends PreferenceActivity accentColor)); boolean fPassCodeEnabled = getResources().getBoolean(R.bool.passcode_enabled); - boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials); + boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials_enabled); boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light); @@ -577,30 +577,33 @@ public class Preferences extends PreferenceActivity private void setupDeviceCredentialsPreference(PreferenceCategory preferenceCategoryDetails, boolean deviceCredentialsEnabled) { SwitchPreference useDeviceCredentials = (SwitchPreference) findPreference(PREFERENCE_USE_DEVICE_CREDENTIALS); + + final Activity activity = this; + if (useDeviceCredentials != null && deviceCredentialsEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (!DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { - DisplayUtils.showSnackMessage(this, R.string.prefs_device_credentials_not_setup); - useDeviceCredentials.setChecked(false); - } else { - useDeviceCredentials.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - Boolean incoming = (Boolean) newValue; - if (incoming) { - SharedPreferences appPrefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - SharedPreferences.Editor editor = appPrefs.edit(); - editor.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, true).apply(); - return true; - } else { - Intent i = new Intent(getApplicationContext(), RequestCredentialsActivity.class); - startActivityForResult(i, ACTION_CONFIRM_DEVICE_CREDENTIALS); + useDeviceCredentials.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + Boolean incoming = (Boolean) newValue; + if (incoming) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { + DisplayUtils + .showSnackMessage(activity, R.string.prefs_device_credentials_not_setup); return false; } + SharedPreferences appPrefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + appPrefs.edit().putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, true).apply(); + return true; + } else { + Intent i = new Intent(getApplicationContext(), RequestCredentialsActivity.class); + startActivityForResult(i, ACTION_CONFIRM_DEVICE_CREDENTIALS); + return false; } - }); - } + } + }); } else { preferenceCategoryDetails.removePreference(useDeviceCredentials); } diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index 94894980c2..43fdd27855 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -32,6 +32,7 @@ import com.owncloud.android.R; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.AnalyticsUtils; import com.owncloud.android.utils.DeviceCredentialUtils; +import com.owncloud.android.utils.DisplayUtils; /** * Dummy activity that is used to handle the device's default authentication workflow. @@ -62,8 +63,14 @@ public class RequestCredentialsActivity extends Activity { protected void onResume() { super.onResume(); AnalyticsUtils.setCurrentScreenName(this, SCREEN_NAME, TAG); - DeviceCredentialUtils.createKey(getApplicationContext()); - requestCredentials(); + + if (DeviceCredentialUtils.areCredentialsAvailable(this)) { + DeviceCredentialUtils.createKey(getApplicationContext()); + requestCredentials(); + } else { + DisplayUtils.showSnackMessage(this, R.string.prefs_device_credentials_not_setup); + finishWithResult(true); + } } private void requestCredentials() { From ab4444aa229ca83ea880487d02fe9df6587f42c1 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Thu, 9 Nov 2017 13:30:47 +0100 Subject: [PATCH 06/26] fix lint issues --- src/main/res/drawable/ic_fingerprint.xml | 9 --------- src/main/res/values-ar/strings.xml | 2 -- src/main/res/values-b+en+001/strings.xml | 5 ----- src/main/res/values-ca/strings.xml | 2 -- src/main/res/values-cs-rCZ/strings.xml | 3 --- src/main/res/values-da/strings.xml | 2 -- src/main/res/values-de/strings.xml | 4 ---- src/main/res/values-el/strings.xml | 5 ----- src/main/res/values-es-rAR/strings.xml | 5 ----- src/main/res/values-es-rCO/strings.xml | 5 ----- src/main/res/values-es-rCR/strings.xml | 5 ----- src/main/res/values-es-rDO/strings.xml | 5 ----- src/main/res/values-es-rEC/strings.xml | 5 ----- src/main/res/values-es-rMX/strings.xml | 5 ----- src/main/res/values-es/strings.xml | 5 ----- src/main/res/values-fi-rFI/strings.xml | 2 -- src/main/res/values-fr/strings.xml | 5 ----- src/main/res/values-hu-rHU/strings.xml | 5 ----- src/main/res/values-in/strings.xml | 5 ----- src/main/res/values-is/strings.xml | 5 ----- src/main/res/values-it/strings.xml | 5 ----- src/main/res/values-ja-rJP/strings.xml | 5 ----- src/main/res/values-ko/strings.xml | 2 -- src/main/res/values-lt-rLT/strings.xml | 5 ----- src/main/res/values-nb-rNO/strings.xml | 5 ----- src/main/res/values-nl/strings.xml | 5 ----- src/main/res/values-pl/strings.xml | 5 ----- src/main/res/values-pt-rBR/strings.xml | 5 ----- src/main/res/values-ru/strings.xml | 5 ----- src/main/res/values-sk-rSK/strings.xml | 2 -- src/main/res/values-sl/strings.xml | 2 -- src/main/res/values-sq/strings.xml | 5 ----- src/main/res/values-sr/strings.xml | 5 ----- src/main/res/values-sv/strings.xml | 5 ----- src/main/res/values-tr/strings.xml | 5 ----- src/main/res/values-zh-rCN/strings.xml | 5 ----- src/main/res/values-zh-rTW/strings.xml | 2 -- 37 files changed, 162 deletions(-) delete mode 100644 src/main/res/drawable/ic_fingerprint.xml diff --git a/src/main/res/drawable/ic_fingerprint.xml b/src/main/res/drawable/ic_fingerprint.xml deleted file mode 100644 index f650f74452..0000000000 --- a/src/main/res/drawable/ic_fingerprint.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index 8402b3077d..65e2e59e03 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -40,8 +40,6 @@ المزيد إدارة الحسابات الرقم السري - قفل بصمه الاصبع - لم يتم ادخال اى بصمات وضع متقدم عرض الملفات المخفية حذف البيانات التاريخية diff --git a/src/main/res/values-b+en+001/strings.xml b/src/main/res/values-b+en+001/strings.xml index da28712e38..8114e4fdcd 100644 --- a/src/main/res/values-b+en+001/strings.xml +++ b/src/main/res/values-b+en+001/strings.xml @@ -40,8 +40,6 @@ More Manage accounts Passcode lock - Fingerprint lock - No fingerprints have been set up. Expert mode Show hidden files Delete history @@ -602,9 +600,6 @@ Skip - Please scan your finger - Finger not recognized - E-mail Phone number diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 0467235601..91c128c4be 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -40,8 +40,6 @@ Més Gestió de comptes Contrasenya - Bloqueig d\'empremta - No s\'ha configurat cap empremta digital. Mode expert Mostra els fitxers ocults Esborra l\'històric diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index 79b21b457a..25bb88468b 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -567,9 +567,6 @@ Podpora IPv6 Přeskočit - Prosím, naskenujte svůj otisk prstu - Otisk prstu nerozpoznán - E-mail Telefonní číslo diff --git a/src/main/res/values-da/strings.xml b/src/main/res/values-da/strings.xml index bd18740103..a94e87f6b7 100644 --- a/src/main/res/values-da/strings.xml +++ b/src/main/res/values-da/strings.xml @@ -40,8 +40,6 @@ Mere Administrer konti Passcode-lås - Fingeraftrykslås - Ingen fingeraftryk er blevet sat op. Eksperttilstand Vis skjulte filer Slet historik diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 8c97561272..5b2e8fab17 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -40,8 +40,6 @@ Mehr Konten verwalten PIN gesperrt - Fingerabdruck-Sperre - Keine Fingerabdrücke eingerichtet. Expertenmodus Versteckte Dateien anzeigen Verlauf löschen @@ -602,8 +600,6 @@ Überspringen - Bitte scannen Sie Ihren Finger - Finger nicht erkannt E-Mail diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 54dc57c303..93cd64db92 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -40,8 +40,6 @@ Περισσότερα Διαχείριση λογαριασμών Κλείδωμα με κωδικό πρόσβασης - Κλείδωμα με αποτύπωμα - Δεν έχει οριστεί αποτύπωμα Λειτουργία για προχωρημένους Εμφάνιση κρυφών αρχείων Διαγραφή ιστορικού @@ -527,9 +525,6 @@ Υποστήριξη IPv6 Παράλειψη - Παρακαλώ σαρρώστε το αποτύπωμά σας - Το αποτύπωμα δεν αναγνωρίστηκε - E-mail Αριθμός τηλεφώνου diff --git a/src/main/res/values-es-rAR/strings.xml b/src/main/res/values-es-rAR/strings.xml index d4df003591..ff99de9153 100644 --- a/src/main/res/values-es-rAR/strings.xml +++ b/src/main/res/values-es-rAR/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo por huella digital - No se ha establecido ninguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -434,9 +432,6 @@ Saltar - Favor de escanear su dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rCO/strings.xml b/src/main/res/values-es-rCO/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rCO/strings.xml +++ b/src/main/res/values-es-rCO/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rCR/strings.xml b/src/main/res/values-es-rCR/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rCR/strings.xml +++ b/src/main/res/values-es-rCR/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rDO/strings.xml b/src/main/res/values-es-rDO/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rDO/strings.xml +++ b/src/main/res/values-es-rDO/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rEC/strings.xml b/src/main/res/values-es-rEC/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rEC/strings.xml +++ b/src/main/res/values-es-rEC/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rMX/strings.xml b/src/main/res/values-es-rMX/strings.xml index 42cbd6ec67..d0d4cf782e 100644 --- a/src/main/res/values-es-rMX/strings.xml +++ b/src/main/res/values-es-rMX/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -602,9 +600,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 6b2db3a53f..98b127b077 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -40,8 +40,6 @@ Más Gestionar cuentas Bloqueo con código de acceso - Bloqueo con huella dactilar - No se han configurado huellas dactilares Modo experto Mostrar archivos ocultos Eliminar historial @@ -602,9 +600,6 @@ Omitir - Por favor, escanea tu dedo - Dedo no reconocido - Correo electrónico Número de teléfono diff --git a/src/main/res/values-fi-rFI/strings.xml b/src/main/res/values-fi-rFI/strings.xml index de6acdccdb..e6b175e5c5 100644 --- a/src/main/res/values-fi-rFI/strings.xml +++ b/src/main/res/values-fi-rFI/strings.xml @@ -40,8 +40,6 @@ Enemmän Tilien hallinta Suojakoodilukitus - Sormenjälkilukitus - Sormenjälkiä ei ole asetettu Asiantuntijatila Näytä piilotetut tiedostot Poista historia diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 1f475d8f48..2f8fa7ffe0 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -40,8 +40,6 @@ Plus Gestion des comptes Code de sécurité - Verrouillage par empreinte digitale - Aucune empreinte digitale n\'a été installée. Mode expert Afficher les fichiers masqués Effacer le journal @@ -601,9 +599,6 @@ Veuillez contacter votre administrateur Ignorer - Veuillez scanner votre doigt - Doigt non reconnu - Adresse e-mail Numéro de téléphone diff --git a/src/main/res/values-hu-rHU/strings.xml b/src/main/res/values-hu-rHU/strings.xml index 7aaed1a90e..2f0737a0dc 100644 --- a/src/main/res/values-hu-rHU/strings.xml +++ b/src/main/res/values-hu-rHU/strings.xml @@ -40,8 +40,6 @@ Több Fiókok kezelése Számkódos lezárás - Lezárás Újjlenyomattal - Nincs újjlenyomat beállítva. Szakértő mód Rejtett fájlok megjelenítése Előzmények törlése @@ -580,9 +578,6 @@ Lépj kapcsolatba a rendszergazdával Kihagy - Kérlek húzd le az ujjad - Ujj ismeretlen - E-mail Telefonszám diff --git a/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml index a2eca92f15..e990e22d4d 100644 --- a/src/main/res/values-in/strings.xml +++ b/src/main/res/values-in/strings.xml @@ -40,8 +40,6 @@ Lainnya Kelola akun Kunci kode sandi - Kunci Fingerprint - Belum ada fingerprint ditambahkan Mode Ahli Lihat berkas tersembunyi Hapus riwayat @@ -434,9 +432,6 @@ Lewat - Pindai jari anda. - Jari tidak dikenali. - Surel Nomor telepon diff --git a/src/main/res/values-is/strings.xml b/src/main/res/values-is/strings.xml index d624883fa2..c12103a977 100644 --- a/src/main/res/values-is/strings.xml +++ b/src/main/res/values-is/strings.xml @@ -40,8 +40,6 @@ Meira Sýsla með notandaaðganga Læsing með lykilkóða - Fingrafaralás - Engin fingraför hafa verið sett inn. Sérfræðingahamur Sýna faldar skrár Eyða ferli @@ -602,9 +600,6 @@ Sleppa - Skannaðu fingurinn þinn - Fingrafar þekktist ekki - Tölvupóstur Símanúmer diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 6daf66059e..b6a705b5d2 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -40,8 +40,6 @@ Altro Gestisci account Blocco con codice di sicurezza - Blocco con impronta digitale - Non è stata impostata alcuna impronta digitale Modalità esperta Mostra i file nascosti Elimina cronologia @@ -602,9 +600,6 @@ Salta - Esegui la scansione del tuo dito - Dito non riconosciuto - Email Numero di telefono diff --git a/src/main/res/values-ja-rJP/strings.xml b/src/main/res/values-ja-rJP/strings.xml index 35e37120d1..aed24b19ac 100644 --- a/src/main/res/values-ja-rJP/strings.xml +++ b/src/main/res/values-ja-rJP/strings.xml @@ -40,8 +40,6 @@ もっと見る アカウント管理 パスコードロック - 指紋ロック - 指紋は設定されていません。 エキスパートモード 隠しファイルを表示 履歴の削除 @@ -450,9 +448,6 @@ スキップ - 指紋をスキャンしてください - 指紋が認識できません - 電話番号 住所 ウェブサイト diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 142fbb6089..5f8de16063 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -40,8 +40,6 @@ 더 보기 계정 관리 암호 잠금 - 지문 잠금 - 설정된 지문이 없습니다. 전문가 모드 숨겨진 파일 보기 과거 기록 삭제 diff --git a/src/main/res/values-lt-rLT/strings.xml b/src/main/res/values-lt-rLT/strings.xml index f2821fca8d..f81d35a28a 100644 --- a/src/main/res/values-lt-rLT/strings.xml +++ b/src/main/res/values-lt-rLT/strings.xml @@ -40,8 +40,6 @@ Daugiau Tvarkyti paskyras Užrakinimas kodų - Piršto užraktas - Nėra pirštų antspaudo užrakto Eksperto režimas Rodyti paslėptus failus Ištrinti istoriją @@ -458,9 +456,6 @@ Praleisti - Prašome nuskenuoti piršto atspaudą - Atspaudas neatpažintas - El. paštas Telefono numeris diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index 5ac9937b46..227520876b 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -40,8 +40,6 @@ Mer Håndter kontoer Passordlås - Fingeravtrykkslås - Det har ikke blitt satt opp noen fingeravtrykk. Ekspertmodus Vis skjulte filer Slett historikk @@ -598,9 +596,6 @@ Hopp over - Skann fingeren din - Gjenkjenner ikke finger - E-post Telefonnummer diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 817618e66e..563614a832 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -40,8 +40,6 @@ Meer Accounts beheren Toegangscode - Vingerafdrukbeveiliging - Er zijn geen vingerafdrukken ingesteld. Expertmodus Verborgen bestanden weergeven Verwijder geschiedenis @@ -599,9 +597,6 @@ Overslaan - Scan je vinger - Vinger niet herkend - Email Telefoonnummer diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 3c87d55629..4961fb591c 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -40,8 +40,6 @@ Więcej Zarządzaj kontami Blokada kodem PIN - Blokada odciskiem palca - Brak skonfigurowanych odcisków palców Tryb eksperta Pokaż ukryte pliki Wyczyść historię @@ -599,9 +597,6 @@ Wsparcie IPv6 Pomiń - Umieść palec na czytniku odcisku palca - Nie rozpoznano odcisku palca - E-mail Numer telefonu diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 5d13ef892e..31d87c631e 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -40,8 +40,6 @@ Mais Gerenciar contas Bloqueio de código de acesso - Bloqueio por impressão digital - Nenhuma impressão digital foi configurada. Modo avançado Mostrar arquivos ocultos Eliminar histórico @@ -602,9 +600,6 @@ Saltar - Por favor escaneie sua impressão digital - Impressão digital não reconhecida - Email Número do telefone diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index bd628bf19b..1029c2a874 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -40,8 +40,6 @@ Больше Управление аккаунтами Блокировка кодом - Блокировка отпечатком пальца - Отпечатки не были настроены. Экспертный режим Показывать скрытые файлы Удалить историю @@ -610,9 +608,6 @@ Пропустить - Приложите палец - Отпечаток не распознан - Email Номер телефона diff --git a/src/main/res/values-sk-rSK/strings.xml b/src/main/res/values-sk-rSK/strings.xml index 79241c1e94..80d9f87440 100644 --- a/src/main/res/values-sk-rSK/strings.xml +++ b/src/main/res/values-sk-rSK/strings.xml @@ -40,8 +40,6 @@ Viac Spravovať účty Zámok bezpečnostného kódu - Zámok odtlačkom prstu - Neboli nastavené žiadne odtlačky prstov. Expertný režim Zobraziť skryté súbory Vymazať históriu diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index d17c5a3f6a..dc189d1955 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -40,8 +40,6 @@ Več Upravljanje z računi Zakleni dostop s kodo PIN - Ključavnica na prstni odtis - Prstni odtis ni nasavljen. Napredni način Prikaži skrite datoteke Izbriši zgodovino diff --git a/src/main/res/values-sq/strings.xml b/src/main/res/values-sq/strings.xml index 754fd37cf7..2696df65b8 100644 --- a/src/main/res/values-sq/strings.xml +++ b/src/main/res/values-sq/strings.xml @@ -40,8 +40,6 @@ Më tepër Administroni llogaritë Kyçje kodkalimi - Kyçuni me shenjat e gishtave - Shenjat e gishtave nuk janë vendosur Mënyra eksperte Trego skedarët e fshehur Fshini historikun @@ -602,9 +600,6 @@ Kalo - Ju lutemi skanoni gishtin tuaj - Gishti nuk u njoh - E-mail Numri i telefonit diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index a2252dd6c9..e9c80008cb 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -40,8 +40,6 @@ Остало Управљање налозима Закључавање кодом - Закључавање отиском прста - Отисак прста није подешен. Експертски режим Прикажи скривене фајлове Обриши историјат @@ -607,9 +605,6 @@ Прескочи - Молимо скенирајте Ваш прст - Прст није препознат - Адреса е-поште Број телефона diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 700029bef2..5eb990223e 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -40,8 +40,6 @@ Mer Hantera konton Kodlås - Fingeravtryckslås - Inget fingeravtryck har konfigurerats. Expertläge Visa dolda filer Radera historik @@ -499,9 +497,6 @@ Hoppa över - Vänligen skanna ditt finger - Finger ej igenkänt - E-post Telefonnummer diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 9d5b049f02..b6306e1cf5 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -40,8 +40,6 @@ Daha fazla Hesap yönetimi Parola kod kilidi - Parmak izi kilidi - Henüz bir parmak izi ayarlanmamış. Uzman kipi Gizli dosyaları görüntüle Geçmişi sil @@ -602,9 +600,6 @@ Atla - Lütfen parmağınızı tarayın - Parmak tanınamadı - E-posta Telefon numarası diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 079e0d3b89..94d285bf47 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -40,8 +40,6 @@ 更多 管理账号 安全码锁 - 指纹锁 - 未设置指纹。 专家模式 显示隐藏文件 删除历史 @@ -551,9 +549,6 @@ 跳过 - 请扫描你的手指 - 手指未被识别 - 邮件 电话号码 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index dc6105299b..aa72dfec71 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -40,8 +40,6 @@ 更多 管理帳戶 密碼鎖 - 指紋上鎖 - 尚未設置任何指紋。 專家模式 顯示隱藏檔案 刪除記錄 From b1244cad4215106a0e8ab14309a6751f43ef0296 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Wed, 15 Nov 2017 20:47:06 +0100 Subject: [PATCH 07/26] remove unneedded translations --- src/main/res/values-es-rCL/strings.xml | 5 ----- src/main/res/values-es-rGT/strings.xml | 5 ----- src/main/res/values-es-rHN/strings.xml | 5 ----- src/main/res/values-es-rNI/strings.xml | 5 ----- src/main/res/values-es-rPA/strings.xml | 5 ----- src/main/res/values-es-rPE/strings.xml | 5 ----- src/main/res/values-es-rPR/strings.xml | 5 ----- src/main/res/values-es-rPY/strings.xml | 5 ----- src/main/res/values-es-rSV/strings.xml | 5 ----- src/main/res/values-es-rUY/strings.xml | 5 ----- src/main/res/values-ka-rGE/strings.xml | 5 ----- 11 files changed, 55 deletions(-) diff --git a/src/main/res/values-es-rCL/strings.xml b/src/main/res/values-es-rCL/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rCL/strings.xml +++ b/src/main/res/values-es-rCL/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rGT/strings.xml b/src/main/res/values-es-rGT/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rGT/strings.xml +++ b/src/main/res/values-es-rGT/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rHN/strings.xml b/src/main/res/values-es-rHN/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rHN/strings.xml +++ b/src/main/res/values-es-rHN/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rNI/strings.xml b/src/main/res/values-es-rNI/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rNI/strings.xml +++ b/src/main/res/values-es-rNI/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rPA/strings.xml b/src/main/res/values-es-rPA/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rPA/strings.xml +++ b/src/main/res/values-es-rPA/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rPE/strings.xml b/src/main/res/values-es-rPE/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rPE/strings.xml +++ b/src/main/res/values-es-rPE/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rPR/strings.xml b/src/main/res/values-es-rPR/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rPR/strings.xml +++ b/src/main/res/values-es-rPR/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rPY/strings.xml b/src/main/res/values-es-rPY/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rPY/strings.xml +++ b/src/main/res/values-es-rPY/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rSV/strings.xml b/src/main/res/values-es-rSV/strings.xml index 638f962ac7..911ea1ddd7 100644 --- a/src/main/res/values-es-rSV/strings.xml +++ b/src/main/res/values-es-rSV/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -580,9 +578,6 @@ Omitir - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-es-rUY/strings.xml b/src/main/res/values-es-rUY/strings.xml index c168b16a5b..571ceb5a59 100644 --- a/src/main/res/values-es-rUY/strings.xml +++ b/src/main/res/values-es-rUY/strings.xml @@ -40,8 +40,6 @@ Más Administrar cuentas Bloqueo con código de seguridad - Bloqueo cor huella digital - No se ha establecido alguna huella digital. Modo experto Mostrar archivos ocultos Borrar historial @@ -480,9 +478,6 @@ Saltar - Por favor escanea tu dedo - El dedo no ha sido reconocido - Correo electrónico Número telefónico diff --git a/src/main/res/values-ka-rGE/strings.xml b/src/main/res/values-ka-rGE/strings.xml index eee5006391..9705c1c17c 100644 --- a/src/main/res/values-ka-rGE/strings.xml +++ b/src/main/res/values-ka-rGE/strings.xml @@ -40,8 +40,6 @@ მეტი ანგარიშების მართვა ჩაკეტვა პასკოდით - ჩაკეტვა თითის ანაბეჭდით - თითის ანაბეჭდები არ დაყენებულა. ექპერტ-რეჟიმი დამალული ფაილების ჩვენება ისტორიის წაშლა @@ -544,9 +542,6 @@ გამოტოვება - გთხოვთ დაასკანიროთ თქვენი თითი - თითი ვერ იქნა ამოცნობილი - ელ-ფოსტა ტელეფონის ნომერი From 05869f5796a3600b7c06d4fa5b54d6d7fea183ac Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 17 Nov 2017 09:26:33 +0100 Subject: [PATCH 08/26] remove obsolete string --- src/main/res/values-fi-rFI/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/res/values-fi-rFI/strings.xml b/src/main/res/values-fi-rFI/strings.xml index e6b175e5c5..b545490a88 100644 --- a/src/main/res/values-fi-rFI/strings.xml +++ b/src/main/res/values-fi-rFI/strings.xml @@ -527,8 +527,6 @@ Ohita - Skannaa sormesi. - Sormea ei tunnistettu Sähköposti From a98905a9a302711eded3dd5d064bb616fd43dc5a Mon Sep 17 00:00:00 2001 From: Hari Date: Wed, 22 Nov 2017 10:17:09 +0530 Subject: [PATCH 09/26] Make lock options mutually exclusive --- .../authentication/PassCodeManager.java | 9 +- .../ui/activity/ManageSpaceActivity.java | 6 +- .../android/ui/activity/PassCodeActivity.java | 3 - .../android/ui/activity/Preferences.java | 164 ++++++++++-------- .../activity/RequestCredentialsActivity.java | 2 +- src/main/res/values/strings.xml | 10 +- src/main/res/xml/preferences.xml | 11 +- 7 files changed, 114 insertions(+), 91 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index 5fd83f2e01..dcd219f598 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -114,8 +114,10 @@ public class PassCodeManager { } private boolean passCodeIsEnabled() { - SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext()); - return (appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false)); + SharedPreferences appPrefs = PreferenceManager + .getDefaultSharedPreferences(MainApp.getAppContext()); + return (appPrefs.getString(Preferences.PREFERENCE_LOCK, "") + .equals(Preferences.LOCK_PASSCODE)); } private boolean deviceCredentialsShouldBeRequested() { @@ -138,6 +140,7 @@ public class PassCodeManager { private boolean deviceCredentialsAreEnabled() { SharedPreferences appPrefs = PreferenceManager .getDefaultSharedPreferences(MainApp.getAppContext()); - return appPrefs.getBoolean(Preferences.PREFERENCE_USE_DEVICE_CREDENTIALS, false); + return (appPrefs.getString(Preferences.PREFERENCE_LOCK, "") + .equals(Preferences.LOCK_DEVICE_CREDENTIALS)); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java index 21db2dd56e..95006a02fe 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java @@ -94,7 +94,9 @@ public class ManageSpaceActivity extends AppCompatActivity { SharedPreferences appPrefs = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); - boolean passCodeEnable = appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false); + String lockPref = appPrefs.getString(Preferences.PREFERENCE_LOCK, Preferences.LOCK_NONE); + boolean passCodeEnable = appPrefs.getString(Preferences.PREFERENCE_LOCK, "") + .equals(Preferences.LOCK_PASSCODE); String passCodeDigits[] = new String[4]; if (passCodeEnable) { @@ -122,7 +124,7 @@ public class ManageSpaceActivity extends AppCompatActivity { appPrefsEditor.putString(PassCodeActivity.PREFERENCE_PASSCODE_D4, passCodeDigits[3]); } - appPrefsEditor.putBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, passCodeEnable); + appPrefsEditor.putString(Preferences.PREFERENCE_LOCK, lockPref); result = result && appPrefsEditor.commit(); return result; diff --git a/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java b/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java index 23fd23e22e..aa5cd9dc59 100644 --- a/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java @@ -56,9 +56,6 @@ public class PassCodeActivity extends AppCompatActivity { public final static String KEY_PASSCODE = "KEY_PASSCODE"; public final static String KEY_CHECK_RESULT = "KEY_CHECK_RESULT"; - // NOTE: PREFERENCE_SET_PASSCODE must have the same value as preferences.xml-->android:key for passcode preference - public final static String PREFERENCE_SET_PASSCODE = "set_pincode"; - public final static String PREFERENCE_PASSCODE_D = "PrefPinCode"; public final static String PREFERENCE_PASSCODE_D1 = "PrefPinCode1"; public final static String PREFERENCE_PASSCODE_D2 = "PrefPinCode2"; diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 516ab64fd9..98e22336e5 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -76,6 +76,7 @@ import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.ThemeUtils; import java.io.IOException; +import java.util.ArrayList; /** * An Activity that allows the user to change the application's settings. @@ -87,7 +88,11 @@ public class Preferences extends PreferenceActivity private static final String TAG = Preferences.class.getSimpleName(); - public final static String PREFERENCE_USE_DEVICE_CREDENTIALS= "use_device_credentials"; + public static final String PREFERENCE_LOCK= "lock"; + + public static final String LOCK_NONE = "none"; + public static final String LOCK_PASSCODE = "passcode"; + public static final String LOCK_DEVICE_CREDENTIALS = "device_credentials"; public static final String PREFERENCE_EXPERT_MODE = "expert_mode"; @@ -106,13 +111,14 @@ public class Preferences extends PreferenceActivity */ private Uri mUri; - private SwitchPreference pCode; + private ListPreference mLock; private SwitchPreference mShowHiddenFiles; private SwitchPreference mExpertMode; private AppCompatDelegate mDelegate; private ListPreference mPrefStoragePath; private String mStoragePath; + private String pendingLock; public static class PreferenceKeys { public static final String STORAGE_PATH = "storage_path"; @@ -509,9 +515,7 @@ public class Preferences extends PreferenceActivity boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light); - setupPasscodePreference(preferenceCategoryDetails, fPassCodeEnabled); - - setupDeviceCredentialsPreference(preferenceCategoryDetails, fDeviceCredentialsEnabled); + setupLockPreference(preferenceCategoryDetails, fPassCodeEnabled, fDeviceCredentialsEnabled); setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); @@ -574,64 +578,56 @@ public class Preferences extends PreferenceActivity } } - private void setupDeviceCredentialsPreference(PreferenceCategory preferenceCategoryDetails, - boolean deviceCredentialsEnabled) { - SwitchPreference useDeviceCredentials = (SwitchPreference) findPreference(PREFERENCE_USE_DEVICE_CREDENTIALS); + private void setupLockPreference(PreferenceCategory preferenceCategoryDetails, + boolean passCodeEnabled, + boolean deviceCredentialsEnabled) { + mLock = (ListPreference) findPreference(PREFERENCE_LOCK); + if (mLock != null && (passCodeEnabled || deviceCredentialsEnabled)) { + ArrayList lockEntries = new ArrayList<>(3); + 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_device_credentials)); - final Activity activity = this; + ArrayList lockValues = new ArrayList<>(3); + lockValues.add(LOCK_NONE); + lockValues.add(LOCK_PASSCODE); + lockValues.add(LOCK_DEVICE_CREDENTIALS); - if (useDeviceCredentials != null && deviceCredentialsEnabled && Build.VERSION.SDK_INT >= - Build.VERSION_CODES.M) { - useDeviceCredentials.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + if (!passCodeEnabled) { + lockEntries.remove(1); + lockValues.remove(1); + } else if (!deviceCredentialsEnabled || Build.VERSION.SDK_INT < + Build.VERSION_CODES.M) { + lockEntries.remove(2); + lockValues.remove(2); + } + String[] lockEntriesArr = new String[lockEntries.size()]; + lockEntriesArr = lockEntries.toArray(lockEntriesArr); + String[] lockValuesArr = new String[lockValues.size()]; + lockValuesArr = lockValues.toArray(lockValuesArr); + + mLock.setEntries(lockEntriesArr); + mLock.setEntryValues(lockValuesArr); + mLock.setSummary(mLock.getEntry()); + mLock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - Boolean incoming = (Boolean) newValue; - if (incoming) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { - DisplayUtils - .showSnackMessage(activity, R.string.prefs_device_credentials_not_setup); - return false; + public boolean onPreferenceChange(Preference preference, Object o) { + pendingLock = LOCK_NONE; + String oldValue = ((ListPreference) preference).getValue(); + String newValue = (String) o; + if (!oldValue.equals(newValue)) { + if (oldValue.equals(LOCK_NONE)) { + enableLock(newValue); + } else { + pendingLock = newValue; + disableLock(oldValue); } - SharedPreferences appPrefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - appPrefs.edit().putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, true).apply(); - return true; - } else { - Intent i = new Intent(getApplicationContext(), RequestCredentialsActivity.class); - startActivityForResult(i, ACTION_CONFIRM_DEVICE_CREDENTIALS); - return false; } - } - }); - } else { - preferenceCategoryDetails.removePreference(useDeviceCredentials); - } - } - - private void setupPasscodePreference(PreferenceCategory preferenceCategoryDetails, boolean fPassCodeEnabled) { - pCode = (SwitchPreference) findPreference(PassCodeActivity.PREFERENCE_SET_PASSCODE); - if (pCode != null && fPassCodeEnabled) { - pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - Intent i = new Intent(getApplicationContext(), PassCodeActivity.class); - Boolean incoming = (Boolean) newValue; - - i.setAction( - incoming ? PassCodeActivity.ACTION_REQUEST_WITH_RESULT : - PassCodeActivity.ACTION_CHECK_WITH_RESULT - ); - - startActivityForResult(i, incoming ? ACTION_REQUEST_PASSCODE : - ACTION_CONFIRM_PASSCODE); - - // Don't update just yet, we will decide on it in onActivityResult return false; } }); } else { - preferenceCategoryDetails.removePreference(pCode); + preferenceCategoryDetails.removePreference(mLock); } } @@ -683,6 +679,35 @@ public class Preferences extends PreferenceActivity } } + private void enableLock(String lock) { + pendingLock = LOCK_NONE; + if (lock.equals(LOCK_PASSCODE)) { + Intent i = new Intent(getApplicationContext(), PassCodeActivity.class); + i.setAction(PassCodeActivity.ACTION_REQUEST_WITH_RESULT); + startActivityForResult(i, ACTION_REQUEST_PASSCODE); + } else if (lock.equals(LOCK_DEVICE_CREDENTIALS)){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { + DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_not_setup); + } else { + DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_enabled); + mLock.setValue(LOCK_DEVICE_CREDENTIALS); + mLock.setSummary(mLock.getEntry()); + } + } + } + + private void disableLock(String lock) { + if (lock.equals(LOCK_PASSCODE)) { + Intent i = new Intent(getApplicationContext(), PassCodeActivity.class); + i.setAction(PassCodeActivity.ACTION_CHECK_WITH_RESULT); + startActivityForResult(i, ACTION_CONFIRM_PASSCODE); + } else if (lock.equals(LOCK_DEVICE_CREDENTIALS)) { + Intent i = new Intent(getApplicationContext(), RequestCredentialsActivity.class); + startActivityForResult(i, ACTION_CONFIRM_DEVICE_CREDENTIALS); + } + } + private void setupGeneralCategory(int accentColor) { PreferenceCategory preferenceCategoryGeneral = (PreferenceCategory) findPreference("general"); preferenceCategoryGeneral.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_general), @@ -816,14 +841,7 @@ public class Preferences extends PreferenceActivity t.start(); } - @Override - protected void onResume() { - super.onResume(); - SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - boolean state = appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false); - pCode.setChecked(state); - } @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -844,32 +862,32 @@ public class Preferences extends PreferenceActivity for (int i = 1; i <= 4; ++i) { appPrefs.putString(PassCodeActivity.PREFERENCE_PASSCODE_D + i, passcode.substring(i - 1, i)); } - appPrefs.putBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, true); appPrefs.apply(); + mLock.setValue(LOCK_PASSCODE); + mLock.setSummary(mLock.getEntry()); DisplayUtils.showSnackMessage(this, R.string.pass_code_stored); } } else if (requestCode == ACTION_CONFIRM_PASSCODE && resultCode == RESULT_OK) { if (data.getBooleanExtra(PassCodeActivity.KEY_CHECK_RESULT, false)) { - - SharedPreferences.Editor appPrefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); - appPrefs.putBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false); - appPrefs.apply(); + mLock.setValue(LOCK_NONE); + mLock.setSummary(mLock.getEntry()); DisplayUtils.showSnackMessage(this, R.string.pass_code_removed); + if (!pendingLock.equals(LOCK_NONE)) { + enableLock(pendingLock); + } } } else if (requestCode == ACTION_REQUEST_CODE_DAVDROID_SETUP && resultCode == RESULT_OK) { DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_sync_setup_successful); } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && data.getBooleanExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, false)) { - SharedPreferences.Editor appPrefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()).edit(); - appPrefs.putBoolean(PREFERENCE_USE_DEVICE_CREDENTIALS, false).apply(); - SwitchPreference useDeviceCredentials = (SwitchPreference) - findPreference(PREFERENCE_USE_DEVICE_CREDENTIALS); - useDeviceCredentials.setChecked(false); + mLock.setValue(LOCK_NONE); + mLock.setSummary(mLock.getEntry()); DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); + if (!pendingLock.equals(LOCK_NONE)) { + enableLock(pendingLock); + } } } diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index 43fdd27855..f91afab355 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -68,7 +68,7 @@ public class RequestCredentialsActivity extends Activity { DeviceCredentialUtils.createKey(getApplicationContext()); requestCredentials(); } else { - DisplayUtils.showSnackMessage(this, R.string.prefs_device_credentials_not_setup); + DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_not_setup); finishWithResult(true); } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index ca7d734400..e512d0f38b 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -39,9 +39,13 @@ General More Manage accounts - Passcode lock - Lock using device credentials - No device credentials have been set up. + Lock + Lock using + None + Passcode + Device credentials + Device credentials enabled + No device credentials have been set up. Expert mode Show hidden files Delete history diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index e61ec04c97..ce94444bb0 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -39,12 +39,11 @@ - - + From e8a4f3736ceec1474727ea5630ba8c700dd59013 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Fri, 1 Dec 2017 11:23:35 +0100 Subject: [PATCH 10/26] remove unused translations --- src/main/res/values-ar/strings.xml | 1 - src/main/res/values-b+en+001/strings.xml | 1 - src/main/res/values-ca/strings.xml | 1 - src/main/res/values-cs-rCZ/strings.xml | 1 - src/main/res/values-da/strings.xml | 1 - src/main/res/values-de/strings.xml | 1 - src/main/res/values-el/strings.xml | 1 - src/main/res/values-es-rAR/strings.xml | 1 - src/main/res/values-es-rCL/strings.xml | 1 - src/main/res/values-es-rCO/strings.xml | 1 - src/main/res/values-es-rCR/strings.xml | 1 - src/main/res/values-es-rDO/strings.xml | 1 - src/main/res/values-es-rEC/strings.xml | 1 - src/main/res/values-es-rGT/strings.xml | 1 - src/main/res/values-es-rHN/strings.xml | 1 - src/main/res/values-es-rMX/strings.xml | 1 - src/main/res/values-es-rNI/strings.xml | 1 - src/main/res/values-es-rPA/strings.xml | 1 - src/main/res/values-es-rPE/strings.xml | 1 - src/main/res/values-es-rPR/strings.xml | 1 - src/main/res/values-es-rPY/strings.xml | 1 - src/main/res/values-es-rSV/strings.xml | 1 - src/main/res/values-es-rUY/strings.xml | 1 - src/main/res/values-es/strings.xml | 1 - src/main/res/values-eu/strings.xml | 1 - src/main/res/values-fi-rFI/strings.xml | 1 - src/main/res/values-fr/strings.xml | 1 - src/main/res/values-hu-rHU/strings.xml | 1 - src/main/res/values-in/strings.xml | 1 - src/main/res/values-is/strings.xml | 1 - src/main/res/values-it/strings.xml | 1 - src/main/res/values-ja-rJP/strings.xml | 1 - src/main/res/values-ka-rGE/strings.xml | 1 - src/main/res/values-ko/strings.xml | 1 - src/main/res/values-lt-rLT/strings.xml | 1 - src/main/res/values-nb-rNO/strings.xml | 1 - src/main/res/values-nl/strings.xml | 1 - src/main/res/values-pl/strings.xml | 1 - src/main/res/values-pt-rBR/strings.xml | 1 - src/main/res/values-ru/strings.xml | 1 - src/main/res/values-sk-rSK/strings.xml | 1 - src/main/res/values-sl/strings.xml | 1 - src/main/res/values-sq/strings.xml | 1 - src/main/res/values-sr/strings.xml | 1 - src/main/res/values-sv/strings.xml | 1 - src/main/res/values-tr/strings.xml | 1 - src/main/res/values-zh-rCN/strings.xml | 1 - src/main/res/values-zh-rTW/strings.xml | 1 - 48 files changed, 48 deletions(-) diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index 65e2e59e03..a3d7c35553 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -39,7 +39,6 @@ عام المزيد إدارة الحسابات - الرقم السري وضع متقدم عرض الملفات المخفية حذف البيانات التاريخية diff --git a/src/main/res/values-b+en+001/strings.xml b/src/main/res/values-b+en+001/strings.xml index 8114e4fdcd..e4ade8c794 100644 --- a/src/main/res/values-b+en+001/strings.xml +++ b/src/main/res/values-b+en+001/strings.xml @@ -39,7 +39,6 @@ General More Manage accounts - Passcode lock Expert mode Show hidden files Delete history diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 91c128c4be..8a232014c9 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -39,7 +39,6 @@ General Més Gestió de comptes - Contrasenya Mode expert Mostra els fitxers ocults Esborra l\'històric diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index 25bb88468b..ab165657aa 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -39,7 +39,6 @@ Obecné Více Spravovat účty - Zámek bezpečnostního kódu Zámek otiskem prstu Nebyly nastaveny žádné otisky prstů. Režim pro experty diff --git a/src/main/res/values-da/strings.xml b/src/main/res/values-da/strings.xml index a94e87f6b7..fcc062110d 100644 --- a/src/main/res/values-da/strings.xml +++ b/src/main/res/values-da/strings.xml @@ -39,7 +39,6 @@ Generel Mere Administrer konti - Passcode-lås Eksperttilstand Vis skjulte filer Slet historik diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 5b2e8fab17..f6efa415ed 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -39,7 +39,6 @@ Allgemein Mehr Konten verwalten - PIN gesperrt Expertenmodus Versteckte Dateien anzeigen Verlauf löschen diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 93cd64db92..ff8fbecfb7 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -39,7 +39,6 @@ Γενικά Περισσότερα Διαχείριση λογαριασμών - Κλείδωμα με κωδικό πρόσβασης Λειτουργία για προχωρημένους Εμφάνιση κρυφών αρχείων Διαγραφή ιστορικού diff --git a/src/main/res/values-es-rAR/strings.xml b/src/main/res/values-es-rAR/strings.xml index ff99de9153..7ff817933c 100644 --- a/src/main/res/values-es-rAR/strings.xml +++ b/src/main/res/values-es-rAR/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rCL/strings.xml b/src/main/res/values-es-rCL/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rCL/strings.xml +++ b/src/main/res/values-es-rCL/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rCO/strings.xml b/src/main/res/values-es-rCO/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rCO/strings.xml +++ b/src/main/res/values-es-rCO/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rCR/strings.xml b/src/main/res/values-es-rCR/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rCR/strings.xml +++ b/src/main/res/values-es-rCR/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rDO/strings.xml b/src/main/res/values-es-rDO/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rDO/strings.xml +++ b/src/main/res/values-es-rDO/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rEC/strings.xml b/src/main/res/values-es-rEC/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rEC/strings.xml +++ b/src/main/res/values-es-rEC/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rGT/strings.xml b/src/main/res/values-es-rGT/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rGT/strings.xml +++ b/src/main/res/values-es-rGT/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rHN/strings.xml b/src/main/res/values-es-rHN/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rHN/strings.xml +++ b/src/main/res/values-es-rHN/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rMX/strings.xml b/src/main/res/values-es-rMX/strings.xml index d0d4cf782e..133cb30388 100644 --- a/src/main/res/values-es-rMX/strings.xml +++ b/src/main/res/values-es-rMX/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rNI/strings.xml b/src/main/res/values-es-rNI/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rNI/strings.xml +++ b/src/main/res/values-es-rNI/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rPA/strings.xml b/src/main/res/values-es-rPA/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rPA/strings.xml +++ b/src/main/res/values-es-rPA/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rPE/strings.xml b/src/main/res/values-es-rPE/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rPE/strings.xml +++ b/src/main/res/values-es-rPE/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rPR/strings.xml b/src/main/res/values-es-rPR/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rPR/strings.xml +++ b/src/main/res/values-es-rPR/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rPY/strings.xml b/src/main/res/values-es-rPY/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rPY/strings.xml +++ b/src/main/res/values-es-rPY/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rSV/strings.xml b/src/main/res/values-es-rSV/strings.xml index 911ea1ddd7..3295a7cc68 100644 --- a/src/main/res/values-es-rSV/strings.xml +++ b/src/main/res/values-es-rSV/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es-rUY/strings.xml b/src/main/res/values-es-rUY/strings.xml index 571ceb5a59..ea2aafd183 100644 --- a/src/main/res/values-es-rUY/strings.xml +++ b/src/main/res/values-es-rUY/strings.xml @@ -39,7 +39,6 @@ General Más Administrar cuentas - Bloqueo con código de seguridad Modo experto Mostrar archivos ocultos Borrar historial diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 98b127b077..585b303e5e 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -39,7 +39,6 @@ General Más Gestionar cuentas - Bloqueo con código de acceso Modo experto Mostrar archivos ocultos Eliminar historial diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index ecf01d89aa..e712e7f56e 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -39,7 +39,6 @@ Orokorra Gehiago Kudeatu kontuak - Pasakode bidezko blokeoa Hatz-marka bidezko blokeoa Ez da hatz-markarik konfiguratu Modu aurreratua diff --git a/src/main/res/values-fi-rFI/strings.xml b/src/main/res/values-fi-rFI/strings.xml index b545490a88..049d52fb32 100644 --- a/src/main/res/values-fi-rFI/strings.xml +++ b/src/main/res/values-fi-rFI/strings.xml @@ -39,7 +39,6 @@ Yleiset Enemmän Tilien hallinta - Suojakoodilukitus Asiantuntijatila Näytä piilotetut tiedostot Poista historia diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 2f8fa7ffe0..d7153ce60b 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -39,7 +39,6 @@ Général Plus Gestion des comptes - Code de sécurité Mode expert Afficher les fichiers masqués Effacer le journal diff --git a/src/main/res/values-hu-rHU/strings.xml b/src/main/res/values-hu-rHU/strings.xml index 2f0737a0dc..c0c73eb18b 100644 --- a/src/main/res/values-hu-rHU/strings.xml +++ b/src/main/res/values-hu-rHU/strings.xml @@ -39,7 +39,6 @@ Általános Több Fiókok kezelése - Számkódos lezárás Szakértő mód Rejtett fájlok megjelenítése Előzmények törlése diff --git a/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml index e990e22d4d..c3682f1ff4 100644 --- a/src/main/res/values-in/strings.xml +++ b/src/main/res/values-in/strings.xml @@ -39,7 +39,6 @@ Umum Lainnya Kelola akun - Kunci kode sandi Mode Ahli Lihat berkas tersembunyi Hapus riwayat diff --git a/src/main/res/values-is/strings.xml b/src/main/res/values-is/strings.xml index c12103a977..c191ce290e 100644 --- a/src/main/res/values-is/strings.xml +++ b/src/main/res/values-is/strings.xml @@ -39,7 +39,6 @@ Almennt Meira Sýsla með notandaaðganga - Læsing með lykilkóða Sérfræðingahamur Sýna faldar skrár Eyða ferli diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index b6a705b5d2..1f787b1fe4 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -39,7 +39,6 @@ Generale Altro Gestisci account - Blocco con codice di sicurezza Modalità esperta Mostra i file nascosti Elimina cronologia diff --git a/src/main/res/values-ja-rJP/strings.xml b/src/main/res/values-ja-rJP/strings.xml index aed24b19ac..4958f21249 100644 --- a/src/main/res/values-ja-rJP/strings.xml +++ b/src/main/res/values-ja-rJP/strings.xml @@ -39,7 +39,6 @@ 一般 もっと見る アカウント管理 - パスコードロック エキスパートモード 隠しファイルを表示 履歴の削除 diff --git a/src/main/res/values-ka-rGE/strings.xml b/src/main/res/values-ka-rGE/strings.xml index 9705c1c17c..3ed7a1db24 100644 --- a/src/main/res/values-ka-rGE/strings.xml +++ b/src/main/res/values-ka-rGE/strings.xml @@ -39,7 +39,6 @@ ზოგადი მეტი ანგარიშების მართვა - ჩაკეტვა პასკოდით ექპერტ-რეჟიმი დამალული ფაილების ჩვენება ისტორიის წაშლა diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 5f8de16063..28d35e7583 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -39,7 +39,6 @@ 일반 더 보기 계정 관리 - 암호 잠금 전문가 모드 숨겨진 파일 보기 과거 기록 삭제 diff --git a/src/main/res/values-lt-rLT/strings.xml b/src/main/res/values-lt-rLT/strings.xml index f81d35a28a..d052ceacbe 100644 --- a/src/main/res/values-lt-rLT/strings.xml +++ b/src/main/res/values-lt-rLT/strings.xml @@ -39,7 +39,6 @@ Bendras Daugiau Tvarkyti paskyras - Užrakinimas kodų Eksperto režimas Rodyti paslėptus failus Ištrinti istoriją diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index 227520876b..50c1e272dd 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -39,7 +39,6 @@ Generelt Mer Håndter kontoer - Passordlås Ekspertmodus Vis skjulte filer Slett historikk diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 563614a832..177d54868c 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -39,7 +39,6 @@ Algemeen Meer Accounts beheren - Toegangscode Expertmodus Verborgen bestanden weergeven Verwijder geschiedenis diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 4961fb591c..46ed5964a9 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -39,7 +39,6 @@ Ogólne Więcej Zarządzaj kontami - Blokada kodem PIN Tryb eksperta Pokaż ukryte pliki Wyczyść historię diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 31d87c631e..292a060614 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -39,7 +39,6 @@ Geral Mais Gerenciar contas - Bloqueio de código de acesso Modo avançado Mostrar arquivos ocultos Eliminar histórico diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 1029c2a874..8ea7f7670b 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -39,7 +39,6 @@ Основные Больше Управление аккаунтами - Блокировка кодом Экспертный режим Показывать скрытые файлы Удалить историю diff --git a/src/main/res/values-sk-rSK/strings.xml b/src/main/res/values-sk-rSK/strings.xml index 80d9f87440..2632eb2522 100644 --- a/src/main/res/values-sk-rSK/strings.xml +++ b/src/main/res/values-sk-rSK/strings.xml @@ -39,7 +39,6 @@ Všeobecné Viac Spravovať účty - Zámok bezpečnostného kódu Expertný režim Zobraziť skryté súbory Vymazať históriu diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index dc189d1955..a0ded1ae1c 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -39,7 +39,6 @@ Splošno Več Upravljanje z računi - Zakleni dostop s kodo PIN Napredni način Prikaži skrite datoteke Izbriši zgodovino diff --git a/src/main/res/values-sq/strings.xml b/src/main/res/values-sq/strings.xml index 2696df65b8..db96338d77 100644 --- a/src/main/res/values-sq/strings.xml +++ b/src/main/res/values-sq/strings.xml @@ -39,7 +39,6 @@ Të përgjithshme Më tepër Administroni llogaritë - Kyçje kodkalimi Mënyra eksperte Trego skedarët e fshehur Fshini historikun diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index e9c80008cb..e785052dbd 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -39,7 +39,6 @@ Опште Остало Управљање налозима - Закључавање кодом Експертски режим Прикажи скривене фајлове Обриши историјат diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 5eb990223e..6ea9a349fc 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -39,7 +39,6 @@ Allmänt Mer Hantera konton - Kodlås Expertläge Visa dolda filer Radera historik diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index b6306e1cf5..af9b2134a2 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -39,7 +39,6 @@ Genel Daha fazla Hesap yönetimi - Parola kod kilidi Uzman kipi Gizli dosyaları görüntüle Geçmişi sil diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 94d285bf47..4fbeedd268 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -39,7 +39,6 @@ 常规 更多 管理账号 - 安全码锁 专家模式 显示隐藏文件 删除历史 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index aa72dfec71..5d5f757e5f 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -39,7 +39,6 @@ 一般 更多 管理帳戶 - 密碼鎖 專家模式 顯示隱藏檔案 刪除記錄 From 2b67067bc9ea6a9b625ffbf12c539961b0c66ba8 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 12 Dec 2017 14:33:16 +0100 Subject: [PATCH 11/26] removed unneeded translations --- src/main/res/values-b+en+001/strings.xml | 1 - src/main/res/values-cs-rCZ/strings.xml | 1 - src/main/res/values-de/strings.xml | 1 - src/main/res/values-es-rCL/strings.xml | 1 - src/main/res/values-es-rCO/strings.xml | 1 - src/main/res/values-es-rCR/strings.xml | 1 - src/main/res/values-es-rDO/strings.xml | 1 - src/main/res/values-es-rEC/strings.xml | 1 - src/main/res/values-es-rGT/strings.xml | 1 - src/main/res/values-es-rHN/strings.xml | 1 - src/main/res/values-es-rMX/strings.xml | 1 - src/main/res/values-es-rNI/strings.xml | 1 - src/main/res/values-es-rPA/strings.xml | 1 - src/main/res/values-es-rPE/strings.xml | 1 - src/main/res/values-es-rPR/strings.xml | 1 - src/main/res/values-es-rPY/strings.xml | 1 - src/main/res/values-es-rSV/strings.xml | 1 - src/main/res/values-es-rUY/strings.xml | 1 - src/main/res/values-es/strings.xml | 1 - src/main/res/values-fr/strings.xml | 1 - src/main/res/values-hu-rHU/strings.xml | 1 - src/main/res/values-it/strings.xml | 1 - src/main/res/values-ka-rGE/strings.xml | 1 - src/main/res/values-nb-rNO/strings.xml | 1 - src/main/res/values-nl/strings.xml | 1 - src/main/res/values-pl/strings.xml | 1 - src/main/res/values-pt-rBR/strings.xml | 1 - src/main/res/values-ru/strings.xml | 1 - src/main/res/values-sr/strings.xml | 1 - src/main/res/values-tr/strings.xml | 1 - src/main/res/values-zh-rTW/strings.xml | 2 -- src/main/res/values/strings.xml | 1 - 32 files changed, 33 deletions(-) diff --git a/src/main/res/values-b+en+001/strings.xml b/src/main/res/values-b+en+001/strings.xml index e4ade8c794..7f5d120503 100644 --- a/src/main/res/values-b+en+001/strings.xml +++ b/src/main/res/values-b+en+001/strings.xml @@ -672,7 +672,6 @@ Activity icon File icon User icon - Fingerprint icon Favourite icon Shared via link icon Synced icon diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index ab165657aa..7ca8b6ef29 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -629,7 +629,6 @@ Ikona Aktivita Ikona Soubor Ikona Uživatel - Ikona Otisk prstu Ikona Oblíbené Ikona Sdíleno odkazem Ikona Synchronizováno diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index f6efa415ed..237916762f 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -674,7 +674,6 @@ Aktivitäten-Icon Datei-Icon Nutzer-Icon - Fingerabdruck-Icon Favoriten-Icon Geteilt mittels Link Icon Synchronisiert-Icon diff --git a/src/main/res/values-es-rCL/strings.xml b/src/main/res/values-es-rCL/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rCL/strings.xml +++ b/src/main/res/values-es-rCL/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rCO/strings.xml b/src/main/res/values-es-rCO/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rCO/strings.xml +++ b/src/main/res/values-es-rCO/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rCR/strings.xml b/src/main/res/values-es-rCR/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rCR/strings.xml +++ b/src/main/res/values-es-rCR/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rDO/strings.xml b/src/main/res/values-es-rDO/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rDO/strings.xml +++ b/src/main/res/values-es-rDO/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rEC/strings.xml b/src/main/res/values-es-rEC/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rEC/strings.xml +++ b/src/main/res/values-es-rEC/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rGT/strings.xml b/src/main/res/values-es-rGT/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rGT/strings.xml +++ b/src/main/res/values-es-rGT/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rHN/strings.xml b/src/main/res/values-es-rHN/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rHN/strings.xml +++ b/src/main/res/values-es-rHN/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rMX/strings.xml b/src/main/res/values-es-rMX/strings.xml index 133cb30388..eb8ec7f452 100644 --- a/src/main/res/values-es-rMX/strings.xml +++ b/src/main/res/values-es-rMX/strings.xml @@ -672,7 +672,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rNI/strings.xml b/src/main/res/values-es-rNI/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rNI/strings.xml +++ b/src/main/res/values-es-rNI/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rPA/strings.xml b/src/main/res/values-es-rPA/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rPA/strings.xml +++ b/src/main/res/values-es-rPA/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rPE/strings.xml b/src/main/res/values-es-rPE/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rPE/strings.xml +++ b/src/main/res/values-es-rPE/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rPR/strings.xml b/src/main/res/values-es-rPR/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rPR/strings.xml +++ b/src/main/res/values-es-rPR/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rPY/strings.xml b/src/main/res/values-es-rPY/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rPY/strings.xml +++ b/src/main/res/values-es-rPY/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rSV/strings.xml b/src/main/res/values-es-rSV/strings.xml index 3295a7cc68..e8a1fee1e5 100644 --- a/src/main/res/values-es-rSV/strings.xml +++ b/src/main/res/values-es-rSV/strings.xml @@ -650,7 +650,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es-rUY/strings.xml b/src/main/res/values-es-rUY/strings.xml index ea2aafd183..8c2ad548c4 100644 --- a/src/main/res/values-es-rUY/strings.xml +++ b/src/main/res/values-es-rUY/strings.xml @@ -537,7 +537,6 @@ Ícono de actividad Ícono de archivo Ícono de usuario - Ícono de huella digital Ícono de Favorito Ícono de compartido por liga Ícono de sincronizado diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 585b303e5e..b2a553ae2b 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -672,7 +672,6 @@ Icono de actividad Icono de archivo Icono de usuario - Icono de lector de huellas Icono de favorito Icono de compartido vía enlace Icono de sincronizado diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index d7153ce60b..4f9d8b7dfa 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -671,7 +671,6 @@ Veuillez contacter votre administrateur Icône d\'activité Icône de fichier Icône d\'utilisateur - Icône de fingerprint Icône des favoris Icône de partage par lien Icône synchronisé diff --git a/src/main/res/values-hu-rHU/strings.xml b/src/main/res/values-hu-rHU/strings.xml index c0c73eb18b..029516b61f 100644 --- a/src/main/res/values-hu-rHU/strings.xml +++ b/src/main/res/values-hu-rHU/strings.xml @@ -668,7 +668,6 @@ A Nextcloud itt érhető el: https://nextcloud.com Aktivitás ikon Fájl ikon Felhasználó ikon - Ujjlenyomat ikon Kedvencek ikon Link ikonon keresztül megosztva Szinkronizálva ikon diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 1f787b1fe4..809eb85a69 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -673,7 +673,6 @@ Icona attività Icona file Icona utente - Icona impronta digitale Icona preferiti Icona condivisi tramite collegamento Icona sincronizzati diff --git a/src/main/res/values-ka-rGE/strings.xml b/src/main/res/values-ka-rGE/strings.xml index 3ed7a1db24..4bd5eca824 100644 --- a/src/main/res/values-ka-rGE/strings.xml +++ b/src/main/res/values-ka-rGE/strings.xml @@ -611,7 +611,6 @@ აქტივობის პიქტოგრამა ფაილის პიქტოგრამა მომხმარებლის პიქტოგრამა - თითის ანაბეჭდის პიქტოგრამა რჩეულის პიქტოგრამა გაზიარებულია ბმულის პიქტოგრამით სინქრონიზირებული პიქტოგრამა diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index 50c1e272dd..64d056243e 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -685,7 +685,6 @@ Sjekk ut Nextcloud på https://nextcloud.com Aktivitetsikon Filikon Brukerikon - Fingeravtrykksikon Favorittikon Delt via lenkeikon Synkronisert-ikon diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 177d54868c..b20da31460 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -669,7 +669,6 @@ Activiteitspictogram Bestandpictogram Gebruikerspictogram - Vingerafdrukpictogram Favorietenpictogram Gedeeld via link icoon Gesynch\'d pictogram diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 46ed5964a9..7e7aa68636 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -658,7 +658,6 @@ Ikona aktywności Ikona pliku Ikona użytkownika - Ikona odcisku palca Ikona ulubionych Ikona udostępniania przez link Ikona synchronizacji diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 292a060614..0eb93203d5 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -673,7 +673,6 @@ Ícone de atividade Ícone de arquivo Ícone de usuário - Ícone de impressão digital Ícone de favorito Ícone de compartilhamento via link Ícone de sincronizado diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 8ea7f7670b..71356d54c8 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -680,7 +680,6 @@ Значок события Значок файла Значок пользователя - Значок отпечатка пальца Значок избранного Значок доступного по ссылке Значок синхронизированного содержимого diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index e785052dbd..623c6b91a9 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -678,7 +678,6 @@ Икона активности Икона фајла Икона корисника - Икона отиска прста Икона омиљених Икона дељења преко везе Икона синхронизоване ставке diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index af9b2134a2..81753ba813 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -673,7 +673,6 @@ İşlem simgesi Dosya simgesi Kullanıcı simgesi - Parmakizi simgesi Sık kullanılanlar simgesi Bağlantı ile paylaşılmış simgesi Eşitlenmiş simgesi diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 5d5f757e5f..d7438efbaa 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -479,8 +479,6 @@ 略過 - 請掃描您的指紋 - 不認識的指紋 E-mail diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e512d0f38b..a815d7872c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -697,7 +697,6 @@ Activity icon File icon User icon - Fingerprint icon Favorite icon Shared via link icon Synced icon From cadf4c0ba5802414655faa7256ad41784ce1721a Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Tue, 20 Mar 2018 12:02:47 +0100 Subject: [PATCH 12/26] - show device credentials only when set up - allow press back/cancel during device credentials Signed-off-by: tobiaskaminsky --- .../authentication/PassCodeManager.java | 24 +++++++++---------- .../android/ui/activity/DrawerActivity.java | 11 +++++++++ .../android/ui/activity/Preferences.java | 8 ++++--- .../activity/RequestCredentialsActivity.java | 13 ++++++---- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index dcd219f598..e2483c0b9b 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -41,6 +41,8 @@ public class PassCodeManager { private static final Set exemptOfPasscodeActivities; + public static final int PASSCODE_ACTIVITY = 9999; + static { exemptOfPasscodeActivities = new HashSet(); exemptOfPasscodeActivities.add(PassCodeActivity.class); @@ -66,7 +68,7 @@ public class PassCodeManager { protected PassCodeManager() {} public void onActivityCreated(Activity activity) { - if (passCodeIsEnabled() || deviceCredentialsAreEnabled()) { + if (passCodeIsEnabled() || deviceCredentialsAreEnabled(activity)) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } else { activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); @@ -79,16 +81,16 @@ public class PassCodeManager { Intent i = new Intent(MainApp.getAppContext(), PassCodeActivity.class); i.setAction(PassCodeActivity.ACTION_CHECK); i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - activity.startActivity(i); + activity.startActivityForResult(i, PASSCODE_ACTIVITY); } if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && Build.VERSION.SDK_INT >= - Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested() && + Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested(activity) && !DeviceCredentialUtils.tryEncrypt(activity)) { Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - activity.startActivity(i); + activity.startActivityForResult(i, PASSCODE_ACTIVITY); } visibleActivitiesCounter++; // keep it AFTER passCodeShouldBeRequested was checked @@ -100,7 +102,7 @@ public class PassCodeManager { } setUnlockTimestamp(); PowerManager powerMgr = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); - if ((passCodeIsEnabled() || deviceCredentialsAreEnabled())&& powerMgr != null && !powerMgr.isScreenOn()) { + if ((passCodeIsEnabled() || deviceCredentialsAreEnabled(activity)) && powerMgr != null && !powerMgr.isScreenOn()) { activity.moveTaskToBack(true); } } @@ -120,9 +122,9 @@ public class PassCodeManager { .equals(Preferences.LOCK_PASSCODE)); } - private boolean deviceCredentialsShouldBeRequested() { + private boolean deviceCredentialsShouldBeRequested(Activity activity) { if ((System.currentTimeMillis() - mTimestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { - return deviceCredentialsAreEnabled(); + return deviceCredentialsAreEnabled(activity); } return false; } @@ -137,10 +139,8 @@ public class PassCodeManager { appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false); } - private boolean deviceCredentialsAreEnabled() { - SharedPreferences appPrefs = PreferenceManager - .getDefaultSharedPreferences(MainApp.getAppContext()); - return (appPrefs.getString(Preferences.PREFERENCE_LOCK, "") - .equals(Preferences.LOCK_DEVICE_CREDENTIALS)); + private boolean deviceCredentialsAreEnabled(Activity activity) { + SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(activity); + return appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_DEVICE_CREDENTIALS); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index b62c6a5f62..92ca73c1ae 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -34,6 +34,7 @@ import android.graphics.PorterDuff; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.design.widget.NavigationView; @@ -59,6 +60,7 @@ import com.bumptech.glide.request.target.SimpleTarget; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.authentication.PassCodeManager; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ExternalLinksProvider; import com.owncloud.android.datamodel.OCFile; @@ -1228,6 +1230,15 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU } else { updateAccountList(); } + } else if (requestCode == PassCodeManager.PASSCODE_ACTIVITY && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && data != null) { + int result = data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, + RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE); + + if (result == RequestCredentialsActivity.KEY_CHECK_RESULT_CANCEL) { + Log_OC.d(TAG, "PassCodeManager cancelled"); + super.onBackPressed(); + } } } diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 98e22336e5..925cae78aa 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -596,8 +596,8 @@ public class Preferences extends PreferenceActivity if (!passCodeEnabled) { lockEntries.remove(1); lockValues.remove(1); - } else if (!deviceCredentialsEnabled || Build.VERSION.SDK_INT < - Build.VERSION_CODES.M) { + } else if (!deviceCredentialsEnabled || Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) { lockEntries.remove(2); lockValues.remove(2); } @@ -881,7 +881,9 @@ public class Preferences extends PreferenceActivity DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_sync_setup_successful); } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - data.getBooleanExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, false)) { + data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, + RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) == + RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) { mLock.setValue(LOCK_NONE); mLock.setSummary(mLock.getEntry()); DisplayUtils.showSnackMessage(this, R.string.credentials_disabled); diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index f91afab355..405af8453b 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -44,6 +44,9 @@ public class RequestCredentialsActivity extends Activity { private static final String SCREEN_NAME = "Device credentials"; public final static String KEY_CHECK_RESULT = "KEY_CHECK_RESULT"; + public final static int KEY_CHECK_RESULT_TRUE = 1; + public final static int KEY_CHECK_RESULT_FALSE = 0; + public final static int KEY_CHECK_RESULT_CANCEL = -1; private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; @Override @@ -51,7 +54,9 @@ public class RequestCredentialsActivity extends Activity { if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { if (resultCode == Activity.RESULT_OK && DeviceCredentialUtils .tryEncrypt(getApplicationContext())) { - finishWithResult(true); + finishWithResult(KEY_CHECK_RESULT_TRUE); + } else if (resultCode == Activity.RESULT_CANCELED) { + finishWithResult(KEY_CHECK_RESULT_CANCEL); } else { Toast.makeText(this, R.string.default_credentials_wrong, Toast.LENGTH_SHORT).show(); requestCredentials(); @@ -69,7 +74,7 @@ public class RequestCredentialsActivity extends Activity { requestCredentials(); } else { DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_not_setup); - finishWithResult(true); + finishWithResult(KEY_CHECK_RESULT_TRUE); } } @@ -80,11 +85,11 @@ public class RequestCredentialsActivity extends Activity { startActivityForResult(i, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); } else { Log_OC.e(TAG, "Keyguard manager is null"); - finishWithResult(false); + finishWithResult(KEY_CHECK_RESULT_FALSE); } } - private void finishWithResult(boolean success) { + private void finishWithResult(int success) { Intent resultIntent = new Intent(); resultIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); resultIntent.putExtra(KEY_CHECK_RESULT, success); From 5f89a1f626e7145e6131d8b13789d61cc4eef022 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Tue, 20 Mar 2018 13:03:52 +0100 Subject: [PATCH 13/26] - use device credential if fingerprint was used on old version - draft for what's new Signed-off-by: tobiaskaminsky --- .../android/authentication/PassCodeManager.java | 11 ++++++----- .../com/owncloud/android/features/FeatureList.java | 6 ++++++ .../com/owncloud/android/ui/activity/Preferences.java | 1 + src/main/res/values/strings.xml | 2 ++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index e2483c0b9b..6744b79419 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -116,10 +116,8 @@ public class PassCodeManager { } private boolean passCodeIsEnabled() { - SharedPreferences appPrefs = PreferenceManager - .getDefaultSharedPreferences(MainApp.getAppContext()); - return (appPrefs.getString(Preferences.PREFERENCE_LOCK, "") - .equals(Preferences.LOCK_PASSCODE)); + SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext()); + return appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_PASSCODE); } private boolean deviceCredentialsShouldBeRequested(Activity activity) { @@ -141,6 +139,9 @@ public class PassCodeManager { private boolean deviceCredentialsAreEnabled(Activity activity) { SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(activity); - return appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_DEVICE_CREDENTIALS); + return appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_DEVICE_CREDENTIALS) || + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + (appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false) + && DeviceCredentialUtils.areCredentialsAvailable(activity)); } } diff --git a/src/main/java/com/owncloud/android/features/FeatureList.java b/src/main/java/com/owncloud/android/features/FeatureList.java index cf324c6453..a326fcca16 100644 --- a/src/main/java/com/owncloud/android/features/FeatureList.java +++ b/src/main/java/com/owncloud/android/features/FeatureList.java @@ -41,6 +41,7 @@ public class FeatureList { private static final int VERSION_1_0_0 = 10000099; private static final int VERSION_3_0_0 = 30000099; + private static final int VERSION_3_1_0 = 30010000; private static final int BETA_VERSION_0 = 0; static public ArrayList get(boolean isMultiAccount) { @@ -69,6 +70,11 @@ public class FeatureList { R.string.whats_new_ipv6_content, VERSION_3_0_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); + // 3.1.0 + featuresList.add(new FeatureItem(R.drawable.whats_new_end_to_end_encryption, + R.string.whats_new_device_credentials_title, R.string.whats_new_device_credentials_content, + VERSION_3_1_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); + return featuresList; } diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 925cae78aa..9704b9263e 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -94,6 +94,7 @@ public class Preferences extends PreferenceActivity public static final String LOCK_PASSCODE = "passcode"; public static final String LOCK_DEVICE_CREDENTIALS = "device_credentials"; + public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint"; public static final String PREFERENCE_EXPERT_MODE = "expert_mode"; private static final int ACTION_REQUEST_PASSCODE = 5; diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index a815d7872c..02edde5f4c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -794,4 +794,6 @@ Adding sharee failed Unsharing failed Updating share failed + Use Android device protection + Use android internal device protection, e.g. pattern, password, fingerprint From f85e8e3389bc8a73fa793bc2e19bde6811cb55f1 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Tue, 20 Mar 2018 14:12:32 +0100 Subject: [PATCH 14/26] code cleanup Signed-off-by: tobiaskaminsky --- .../android/authentication/PassCodeManager.java | 8 ++++---- .../android/ui/activity/ManageSpaceActivity.java | 4 ++-- .../android/ui/activity/Preferences.java | 3 +-- .../ui/activity/RequestCredentialsActivity.java | 3 +-- .../android/utils/DeviceCredentialUtils.java | 16 +++++++--------- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index 6744b79419..f3eb3b9dd8 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -84,9 +84,8 @@ public class PassCodeManager { activity.startActivityForResult(i, PASSCODE_ACTIVITY); } - - if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && Build.VERSION.SDK_INT >= - Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested(activity) && + if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested(activity) && !DeviceCredentialUtils.tryEncrypt(activity)) { Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); @@ -102,7 +101,8 @@ public class PassCodeManager { } setUnlockTimestamp(); PowerManager powerMgr = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); - if ((passCodeIsEnabled() || deviceCredentialsAreEnabled(activity)) && powerMgr != null && !powerMgr.isScreenOn()) { + if ((passCodeIsEnabled() || deviceCredentialsAreEnabled(activity)) && powerMgr != null + && !powerMgr.isScreenOn()) { activity.moveTaskToBack(true); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java index 95006a02fe..15014110d7 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java @@ -52,10 +52,10 @@ public class ManageSpaceActivity extends AppCompatActivity { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setTitle(R.string.manage_space_title); - TextView descriptionTextView = (TextView) findViewById(R.id.general_description); + TextView descriptionTextView = findViewById(R.id.general_description); descriptionTextView.setText(getString(R.string.manage_space_description, getString(R.string.app_name))); - Button clearDataButton = (Button) findViewById(R.id.clearDataButton); + Button clearDataButton = findViewById(R.id.clearDataButton); clearDataButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index 9704b9263e..f80a168403 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -376,7 +376,6 @@ public class Preferences extends PreferenceActivity if (pFeedback != null) { if (feedbackEnabled) { pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override public boolean onPreferenceClick(Preference preference) { String feedbackMail = getString(R.string.mail_feedback); @@ -883,7 +882,7 @@ public class Preferences extends PreferenceActivity } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, - RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) == + RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) == RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) { mLock.setValue(LOCK_NONE); mLock.setSummary(mLock.getEntry()); diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index 405af8453b..456119bf5c 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -52,8 +52,7 @@ public class RequestCredentialsActivity extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { - if (resultCode == Activity.RESULT_OK && DeviceCredentialUtils - .tryEncrypt(getApplicationContext())) { + if (resultCode == Activity.RESULT_OK && DeviceCredentialUtils.tryEncrypt(getApplicationContext())) { finishWithResult(KEY_CHECK_RESULT_TRUE); } else if (resultCode == Activity.RESULT_CANCELED) { finishWithResult(KEY_CHECK_RESULT_CANCEL); diff --git a/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java b/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java index e8def67369..44a69145ef 100644 --- a/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java +++ b/src/main/java/com/owncloud/android/utils/DeviceCredentialUtils.java @@ -62,8 +62,8 @@ public class DeviceCredentialUtils { private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; public static boolean areCredentialsAvailable(Context context) { - KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService( - Context.KEYGUARD_SERVICE); + KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + if (keyguardManager != null) { return keyguardManager.isKeyguardSecure(); } else { @@ -82,13 +82,12 @@ public class DeviceCredentialUtils { try { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); - KeyGenerator keyGenerator = KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); + KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); // Set the alias of the entry in Android KeyStore where the key will appear // and the constrains (purposes) in the constructor of the Builder - keyGenerator.init(new KeyGenParameterSpec.Builder(keyName, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + keyGenerator.init(new KeyGenParameterSpec.Builder(keyName, KeyProperties.PURPOSE_ENCRYPT | + KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) // Require that the user has unlocked in the last 30 seconds @@ -118,9 +117,8 @@ public class DeviceCredentialUtils { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); SecretKey secretKey = (SecretKey) keyStore.getKey(keyName, null); - Cipher cipher = Cipher.getInstance( - KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" - + KeyProperties.ENCRYPTION_PADDING_PKCS7); + Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); // Try encrypting something, it will only work if the user authenticated within // the last AUTHENTICATION_DURATION_SECONDS seconds. From 58d62ab20402abe716874a873fe7542b78102651 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Tue, 20 Mar 2018 16:25:59 +0100 Subject: [PATCH 15/26] updated whatsnew image Signed-off-by: tobiaskaminsky --- .../whats_new_device_credentials.svg | 135 ++++++++++++++++++ .../android/features/FeatureList.java | 2 +- .../whats_new_device_credentials.png | Bin 0 -> 35608 bytes src/main/res/values/strings.xml | 2 +- 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 drawable_resources/whats_new_device_credentials.svg create mode 100644 src/main/res/drawable-hdpi/whats_new_device_credentials.png diff --git a/drawable_resources/whats_new_device_credentials.svg b/drawable_resources/whats_new_device_credentials.svg new file mode 100644 index 0000000000..3bed025b96 --- /dev/null +++ b/drawable_resources/whats_new_device_credentials.svg @@ -0,0 +1,135 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/owncloud/android/features/FeatureList.java b/src/main/java/com/owncloud/android/features/FeatureList.java index a326fcca16..f458dc1db1 100644 --- a/src/main/java/com/owncloud/android/features/FeatureList.java +++ b/src/main/java/com/owncloud/android/features/FeatureList.java @@ -71,7 +71,7 @@ public class FeatureList { BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); // 3.1.0 - featuresList.add(new FeatureItem(R.drawable.whats_new_end_to_end_encryption, + featuresList.add(new FeatureItem(R.drawable.whats_new_device_credentials, R.string.whats_new_device_credentials_title, R.string.whats_new_device_credentials_content, VERSION_3_1_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); diff --git a/src/main/res/drawable-hdpi/whats_new_device_credentials.png b/src/main/res/drawable-hdpi/whats_new_device_credentials.png new file mode 100644 index 0000000000000000000000000000000000000000..dc37936d44a8e9ee7cd2212a1bff7d195564899f GIT binary patch literal 35608 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 XXubV?$QKNP)FUnsharing failed Updating share failed Use Android device protection - Use android internal device protection, e.g. pattern, password, fingerprint + Use android internal device protection, e.g. pattern, password, pin or fingerprint. No need to re-setup it again. From eced47e3f1e12d753fd61da18058aa683046239f Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Tue, 20 Mar 2018 16:30:47 +0100 Subject: [PATCH 16/26] ignore unused resources on translated files (done via transifex) Signed-off-by: tobiaskaminsky --- lint.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/lint.xml b/lint.xml index 5b673ca03b..08b06978e8 100644 --- a/lint.xml +++ b/lint.xml @@ -29,6 +29,7 @@ + From 4a3197d8ddce91368b90180ef3f45220401baa74 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Wed, 21 Mar 2018 08:03:09 +0100 Subject: [PATCH 17/26] enhance what's new screen Signed-off-by: tobiaskaminsky --- .../whats_new_device_credentials.svg | 194 +++++++----------- .../whats_new_device_credentials.png | Bin 35608 -> 25586 bytes src/main/res/values/strings.xml | 2 +- 3 files changed, 79 insertions(+), 117 deletions(-) diff --git a/drawable_resources/whats_new_device_credentials.svg b/drawable_resources/whats_new_device_credentials.svg index 3bed025b96..01aaefa5c5 100644 --- a/drawable_resources/whats_new_device_credentials.svg +++ b/drawable_resources/whats_new_device_credentials.svg @@ -2,8 +2,8 @@ @@ -11,7 +11,7 @@ image/svg+xml - + @@ -19,117 +19,79 @@ - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:zoom="0.43457604" inkscape:cx="55.463573" inkscape:cy="381.67998" inkscape:window-x="0" + inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg4" fit-margin-top="10" + fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" /> + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/drawable-hdpi/whats_new_device_credentials.png b/src/main/res/drawable-hdpi/whats_new_device_credentials.png index dc37936d44a8e9ee7cd2212a1bff7d195564899f..771b875d8b542e5be305b7dc0f4b9ef482ee2c73 100644 GIT binary patch literal 25586 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
SJgd`Rj&7Vg~jt!x%_v{@*tQOYgoJo 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 literal 35608 zcmY&=cT`i$7cL<}lzJn*O9&t!AX25OgmS3Tr6WiQz4xkDjUX-bt`a~xO7Broq(kUE zB7%{wG^y|KyYKz+SPNK$lbJoU%Y1w9Z}Q^4o(3%yI~5rj87-iRG9)8|>XVU?3tgrJ zUkP1?lE7b={O$q9m%*>_%a1VN-&edf&Hc#8=(;YxA!#CX9N>#={%Q~Wjl7)vgC6=i zk_80?iJ?8+{2U&7JBoSvI_GREvXhZrBLh%(je~R7=Yw*sO`6Ylh9sS@hW_NjQEM7N zD9C?N+>L$v%3jrAB<{zXuS`+iKSDoK3sYUUr(jBRyK4Xu(yFy1zpk4qeDCLL{xKxd zx!~{F*#iY9og7KY)%AehgVWH;Nj9uxm{Qo#U`;{oM5w8qlao_~8sV1|`!QMd-I=GD z@tEOsap7shon6Fu5HK^>cl&*(KU~<>c`_L;5(Wt z-j&+G4CeLgh~#}p18joKlro=p34&9Ij(a;U_7cNp{zO_*vUZaf?J>SqQg+>`^!nJN z2_>Wl?v0$PnnYtySjFo%a%O~;W`{=bVQr?*iM_`qS;rMsZ{)NIl+6yZ z;3#w@Z{&>CcIK{Qi=Xm%cJiaA=T;x)>BfKQR!^1^om(ljQ2`7^4?-TSVx?Y%o?c>7 zllXG%p3WZcfK=P`leidx5B)fHsoWv_>w_lUv^vvGzxRWD=n!x=OaB7v8|b3B)kk?6 zgfih+Z43W`)BA*5h4CNQGP1JF>5UA+S>za5-A8!O?^ZU%P0~$;V_l`#S0TsHN;c`( zrkI!*Gxe_Zbk3oce4Ha%s){V|waEdSm0o2-6pC>m^3$C-d6^3l3e~?qAl@`w!ZC9N z`mx6sgu14k8X4qLwI%G{naw`dnMqdvJ}Y`VJwORCSfFnm={=^|FTSpdHv1DVNQbs&e4=pd*$jZvvV;x@$BP?`X zvEz?j`YChpFt1`H*e!=aFnW)b{pe24agCmg)b3qo-P~7cPwo(8q}XAye@q4SK@)5j zSlTEIKYuLKvK5xtce-Eix+xjF<(4dsf+* zezw<{;pFnufrdthBLX+^BON+ODUfhD_>||-YLt@H*m{J3YyBRd{SoU@otOYxl|BC0 zMZo!mX6F#|#7TtAa|@3>OlEDJY<-z~DL6yGGf@{ApE6pJOxf&z(V#L%)z5V&%$_Y; zeqtz%iE-j_f1>U{Pwe)1Nsu=vl*rma>Ls1Ejkpvy{lFJvJZBwsw}ixqUwj`Yu80=J1@duY-AY^WpcG;B)^|cm@rv z(ysW^Yw0H)QKGExwy)p$LxB`S*#C0*R3|3rLDyDYdLU*suw$Dz>K=6GgN zah$Fkds}L0q4CfEq`xu*s!j=IIP9VWo8aCV_%6Odt*R=X_=2ID!#Z_h11YLsC;@%Y z5WD$yTqfbN;Qa`7!jTj^{e9ii-_2vLZt7jN+F>dG*-jGXAMX$&uXMvJi?3f(RF1F? zFlIi_y!q|u-51w+5rh-eNch*ffS}%~5P}=AG*P^uc(&+lmXInJlX9_#3w#k#sarg= z%KC(6$M;UBq+_PNKee$(?*?~I4cfyhI)}2`h7>)9{xkNo%j*lEu@{=oA`sl@|5^6< z^;pXnQ}5&F6%7}hhw9(aui5=z90_xA68=4<(EQ}XhYf^%munGs$Sq09H8p}@jDQ)C zc(+UGx}zSD8aY6aHjhcTrJ<5+?4lKL8pMmp0*&9g|(rdr?@$ zWgYT#@mb*;O^LARG#2@OUwrPapzcg-Lp3rmWw22a8RQ@0-7waff3mug89WAxD`hm#%s(Z;(h7RF}MJ zcdAA}vBw+5&_i_BFLz)l;RWEi*7eSqI^g%CLIE@dQ4Jr6H^m{*k(Lq;F7wMihKRi& zqA9WAcR+UES+;qrrbVLq_o$eI;S<&FG|lerf3J+MlN_w|2~JY%1+WDjzoBgM$j`&W z&h|@PIWru2dsG8?tz7jpEHM!|C+U>lboDb=;6AGlj=>4FjjL|YuunZpz3u^Zr*?n5 zl^2*rF{h`6^Q1TY6IJn;?my2SU1&^LEWu&pRcOR5 zxDm@Kbsjr|0B0UAa_!*{$&KF|76^%{geMlODRp9{!h?_O1d04FBe>h@7hCWUN9Y}b zHGUk=6H|INHrW5=(~Fd>y~h|AG7n43&E-;k&6%rHw84&lTS68v6%-BEXImjiC3UUh zD)M4-S((;vIbaaZitq0+O2pQ&)yQtU5-yLQd|YZ4_B}Ruf|~>KJYxe5us~J#_zj1g zvPo0=!iE!(9(*Ju@h(x3(4nZ8Tai;i%SmyfMySLlB-iO}zPgZa zrl-e94OuBfa=@OE&yjPg&Ww#!%(h>ayAphd@D#i7AfYT99B_|Of7Y$gSG-s$hCZ3< z&S4B8bov_ggobX0KlAnmh!VXXTqvdTTXKm^Qr&yprl@xt=C4ZkQ8|8}<1CC$Z=E-rt9|LoYBTUuNFnUf|-N zn<4m3QZ!#|y~q9XO^F_VRzLncqMf^+0JBfCDOi1a4ojvbscz{iZ$UMAztDd6t-nDcb(ILm;bRqAqUU(95QpDi|ISmsBxyJH!HmJB>c4 z6j)6t8Yr%{>vEv-u4P#ds=}}$bTis$d+WAIJ>TQcSLFK%5jZPoUJS_>?=>S}H(bl!K95c#%@CjiOQOO2x=so%&}Ab|`UE(Cd-Id!Ft1xx1NQ zMDim2T?Pm%IM>j5d*eI?9R}{ya(j6`i2Rn>pPD5%l?g z@7wzMclpX8k6rWJ-IsU3qvZgyrrz}$zBB9VU|d^)SR!E#15xXg?pMO;g0K5M7&!9Z zB)hP!pk4ppNHi01p!xfQST?*6J{`W(i;)vSbNuGT*1(e z2qL@C21lq;$S<6Jt}|qjG7y8wLoD@UGV>I!*Y3n(TKSe97$(0^jdr{Bysl=$7j}_! zx4)iDDKYVbkttGzKa=Y94@S+UDqO1t-`fn|`Hsn?Sw{(n8xC=LR#inv3X;&4%@iDw`B6zh0+V zKB?csm6Ij<3@3_IQXGW6qvrQ0IJW8!&BJXV^U8Pl4d?+2@q=d2`cedItB`0(;GFf7 z>J$(EM!rI1ZKRQafl7vyRBu%6f#wV%=)}n>T`uI-njzLSb=ML2lO0UfYCv%F>vo^m zLVAdkSlC05WxS*?tZ5HdZB~{jak(e7$8M9Xj1fKr(EyIf(a>2ls5phGAX7{e)1jny zAgZa&2bNUFSz|G6N@jY=lssM3*6fl%s>kQ29TEHlr8C8w&Jxd~*fU{@)cuqzSQ)=O zv$J^}J111XD+!o>i|VqCkp*TbgT+aU8xtT9ESUAcr{NC}OM~t);Hhjr^vB+{JQrsY zv)9|Od9T_n2|NAw%BXMFM@~;}M}(SI6*r9g61Rvg4+S?lcRWVJmtxur`SidD+~{y{ z6B#4|MmmQ}B_4hpVY_#Wqqc)Rd_UCr{N>3oKg!45 zwG*_vMSZoH_C2NJAKgnfrVV^#NTm;tAN@+i*K_BA*_&kfvy>8LzEaAzOH+ALz3*g+ z5k4*5?0C06%KNE>MrLG2F)B^hpI)pk41{h_Yy-tUKfhM%NW}Q2dk>{~4IM4PxKjm^ zk-qT-!`jRAKPV{f^Ly+`ui>{=@@gm@(xkKY>?S-|ZCdYAe$uW2;p^CE_|E;o3-9c2 z$x12V=0!6YzS*TD{KQ5zP@=OJKRBk~)}sH!P$QNa{sV&nY$|p^62L4OEJ;06V6-z# z#N3ZOIAmH`5Y6uWa5|C)uJ+$}@_8vGVtgciyh5}rH0Kdrcgw5*UZZaxgDZfnCJ7%3 zxm_F=PF!Dmj98ND;M+1pCA?`!;XCenI_j&XFk~JqmjiC}``LwmPu$K`47d)sk#8a6 zZ*8RB=3Eo!FcdJvdcqAU%XgNW+i*w@Yu$bhQ~|oyI}z#ECE*I>J_TauM9(tUX#m{VW;| z$CiJZxa24w$of5S8hAh}N8SVw_ONsrbyoiur4IBMeMVODlURw|gzKY_rmm)dYOWZ` z=rH~~B>H>*4(Q`q^&mQq)eOzT@j&Rw1R)%bbUhVm6NvcyGgX2(KNza-TUiffn$ zQYiW~rZ6U|k3(1Ajcn+~uCrgv^p8ntJHovv75IbK13r6&?B4%)C*f2eC1k5=D8B#j z?f2E?Qd*?$B|3;QoqGz1Lw6Lny{EaMAQ+`{(@(D+f()f^MKK49A$~b*7!$NWpIvsn zZ5Lu^LTJhV=3!!|YZpPt*L3@qJe#~ulS@4(O47z3@Y(NQUUaxnz`I<<`Xn4vG(F_} zMV!hFcln#NO^xAslymx8>BSmd{hW8zx@vLA^%o=I1-QG0O{s1;2xkTs4se7K$nM7`7V3jPqW$>g%|}*rb}DbXRYBo;e&V! zQ3Mt9%T&>HoOE<$Z^Mt8YFS=guQgdWGC^`4@szjKsIJj0hYQDX%Pwz=e}V(S;5FT$ zwMHB%lITPf|1?vFxj*r?B^u`#?GckfCeA%!I>=^5sSmN`?`hMA+MG+JV+At@!&W<8d}88){X}Z1HOOXxMk6TX3K|+ zP(+9gaV3EoVv`H8Kma)q!4#R3^*~j;H0}|}kdR&Ux{cRd1l4sC`>X`8U=HRNN)F|! zQ5&4Huu~x30$3nCiI~p+Fj@bzL18wa7bc|E*?@R(Jf#l(_fG+$Gkx@V@Z5r<0R43RDKuQ)cwukx;XPWUT~N>KbX0UnzSJ?h~G$vdaK?B`?!jY!o0X z1xZkBRg&?eHjVBzAAl!SD(N^oJImZJR}rlC0^;fZ0SnF_B}7uyGwIS81l%6u#`(Fb zq!7}nI}2Qi9LSB_V~gyp>91hW7bmUhd-3aH{n^o-dDed2l2nMat16riL=gK4m|DK2 z>3$z`77T_B?&E}7r*o&`)E3rX)D0vn);cP~Mb27HUBn&>?1C`q^~-|X#LrNy-hE-G zhhV`dFnL!L(06R_)9`A+sruTgNJ?5NCSJ_gnk)!#XtuD*!G1owM+L%M$T5UN?E-Ud zf|!#&7URaZ^aS66SBMb-3Ul*3xrooPV##$6FDwJZk9)emkwQo=syUkV0)>159asmM zJ2TuX9<$ig)?~tqSV46qBJ9Np#lyB_PLJry*^VJWn)2#m*DpY%j0{%BRJd%IwpdgC z0rr6SK7ki)Ui1bj>vTFf(k6s$=#RKa=tuV}8h3X~YPAYM2R)-)PgBS*3g(t$n36{X zCSv^f-h2hIYB=bp=BbZh1ogJn6dBu1<5q~Q*3nzsSFV>A5%gKUZa%V-_JrzCzQ%sV zJAD~V^e2q9(WAP!nxVHi0Mv;Df)1feQsBzWyN%;F=RTzJ&cf09!Y(4te6=8$zj%>* zQqXbt4~Ri>)oforIDeY$vWC8kLMk#I?dQdxDiDsG0l$(&&62eZF;Uu6kauqYy@ zgZ_l`fs&tGoFBb6YmFl+TpHo@gYK()8)9h(BbT3w;-Xf}mwxrxl2%TGkch0rDzIL_ zJUyipMIe-In}s;v>|p+~)jUZmLLBi6oYSoybZi8;)#RMCirCw@&ml1H6r8p1f3udX zrIftC-qjr~gnQngcZ)Vyyv8I;s`>K2du^CZCU^;X4ml5$yN{FbQHHV#>t7cKfCt00 zi+=!^3OEu=@EI{LvOckGULoujRq`zz>t$E)q#dh8JO$;{7EU4v36Kegh>IH^|j@bBL^fT;>Hv9Mv^92g&+jnt+OcOmQAwEtQHYnWAp8A&C69HF?z{ z;Fg|4tSH=5719m!jIV}_V&o(c`F&hT<|SZ$jad4QPsb-h9O=*ZWMJwtK%^3d`szbA2I`SFiqUS{M*dLg|?BC(b?#7evfp8e=ZcD_@o)? z@(ib>`IL8}pNo3?#? zb7U*zwSCpAsU8C8CzDxm$1^Ic`!6m-R<%GsOE6wi4BE-qh*?9n^C2QrZZls@b&ab) z5jY}4XWIyNg^GC_qZCsWk~{^`Y=%q?@tQ@2vg{K!ceGN|dzcozvE0y2*e1t*=+;R! zpS@GZ&rnrVKV?G7ZWLZNS_#abguRn-i5X1L3p6Q@;5EMiNE-@$EA;KYH?zEdIf_at z^vBPUi+b)oh-HSY7x~z&XEN+=X&xn?Pm3-fW{hR;B=`fdG}4P$hr@kfH6=LJXrt&{ z$1XQK8rwjwyd(;{aDP5fha%WApLQ|7>lE8+#D6hAald=H0D=WxyRSj@ZwuS-8b@*! zjgHu?3XlysiBHuzWjkcpKJYEe7F9!?yv^+m5=Ix2au0@KF$4VV{D}O1*5{zVI!^e| z*E>GMP{(9EqWj@QY=`?8t;oq6-oDjDTf;$p^|@{>|xq2+YtO($?5~xw!%< zpithr2cgzvzZQ?&P_syUFLG1p0&`2-oxg|B04>l%F7Mmht(af|Gw;0X2r=;}*>GmL z$KfXT8j&VT3fB;4xk8kn4*T9ZsOKY3IlPt8RK{YmRjFqy|CJ_20%qFs7KXOWzc83Tfa7*lp2#KQC7=ZOl_@j~y7Gjw zIhGmVCH3K>R)6vYGs__nbRsIBH$#3%FRBvE@qD=0XIourJT2p7r7qn)_<3xxW@uD% zwdvyZ145`whM(pC;{u3!( zbkR7`xurmS&5&1ILKYiU}j6L_&zmo}k#2oF=Yb}w_om_%F1fu_6x zVI|9<4280Swb3+PGrH^ri8QF<15@`}Zew#W$RfPy&}9$Col=TP5jPR}L&H)0$o4yg z)#&3G9_&Q7KT1ul&t8=%N7MsYfKc7)>VX%SEc{)3RC1Y%=|%uv4V&IK+z9Hn-khI) z;)ij%U$m^r$l|@t*Uw8xX>M8jSbuJk)j9P85x)l$=9HRikHsiFQ+6K$B2EdV9=+&r z!DJakelqXBX3$Pwaxkfx2%E`QOpALPb>~Q`$+Z(=!wioa7!I-ovhQyu?l}{j%3#>C zcUhtXrufO2=lprR=(RZrSjw`ZY-4N^Q#}Yv-hcexK&ms~R=pTajBaeKE%+Qj8_q5F zD)-B%Boah5Qs`8YGhQ!C_5EhuRc|@(I9!3&jB?POjep?uWYy%pFBP|Fb62JBgDPw< z^yo3llFOrQtGAB0@!=0{Pq*MDhSQCE<{p9l(}ZLmjeF8<egTYj8TD?s8ivkxuSYB3~uHa|fZVdOHT;=MvYTk~8FZ=5XW z0>#;DNf*?|o^i;Pjdyc>_<>)>WSaVPiVC8i{$?NKL0bs)s$*u(&bZlHdO%t1SSFlR6>Ja5s7Y-qp>zO(D1kW@#GR-L{m{j(fx?ZvQ= zaX{P>)?T*E#^52?4r)#wc;Ygh3kXvnKsDJQ%*OoTBv110BUE_HM`bV=RH#{`g9wFV zyWW(m8W4nbqfp~{>=Z(Lqe(}>9g|=dGqWq^F zAJ@jkB$1~m!ZFe#_qZic5)jdvu_a{ipNf7Tsx#p>v^bs`M@HN+Ed00@n24+QD`?Cz zZnGf1QyJKbOW|+VTKHM(k*e;i@@m(sJV&EQ zETb-DbL}=amd=3Ppu9H#n?4k=;N-LcRt3Q_RJF>|jz|*ruB^pAmbqycHp}B52@eKh_`D z;%}@*_)z9~$yHOS#uF^p$$>5T45Y^m(oQV*wR~)y-=-foZBc+-0amK|jhhtrQ`{eZ zuIr^{TFwVtz^vxc8{Bih1)aie4uE61!_(ol)YJE6@VDmJz^@U56ShcNO$#HNFlP^N zkIInfdw-AJvvTiNTqmu>a&zV<*SR&Ma6y3_;K)dA(pix^+5ZiW*3VYbjjPD^s#l7X z^xFJ*ZWflVH)7z+J;KAXs+WIHu@k-E$gN0^3ia=W!m)8CjEUmA_Z3vteDZ$nF-Upk zGlXzR0+yvCau#YktDgn*)pnBD{bXN!AgSkzF({<(G{ zsqUF!eb@8%3s1F!^1xd9TQy|f{cqAo_cs63FT7U&{?=;6~|00x{4?+Aa7Qvc2So;IM#}o34EQ zrN8dLVv_btj%#Q(fCnojx~kjEs1L}?9R#q8nlxH}O2WymDBG@iV$+QbPJ%h<*{LlG zcgz5eAIgGjida(vgOe8i19JnN`vqf^vy5*6r8H?_BBMYnZ@3B<_qZg_3;6xsh z_U-jS69Y!k(5a-@C0=0dhqCM%rxqWi(~T5iTD{1N{xP@el&4D2a+UHV9I6kqcuv+W z|5Y{AMR!Cnbxb{8n-2UTeH$1TW%Zi40hD|NQl#&alp}NG{g!uKfwkoh^#Mcn@ndVX z$rWZmWLepHO}T7RpM5`RxcAa8_7JYhwA6_a06jCm&b;8|MGkhjg=rEDx;ZhA6orLf zl4E`#?I-tVfRZi_Q?NpZW78@+ucj!={IZ*XIRq$9A7rF3O0gTosKrqN|AGcHkN11$|nsP6;6vzIF9j{I8b65;G8E6um0> z{8EkLHbaq8k9_1Kc?37vJ}kfTRjXGiwFsjHW&D@O8)80>O_hepZ)g~Bj+Q+!XK|HU zsGsV(Vi8hM`C)h%UMZg$@Ot-;?VL_|`urpOiWK{n`%3(I_|0TSygB|zl;)8SH*`M{ zBRD%Z)TDWI)0=nrilg}!#R4NdOsy;aq7TXdm*iVwz)#@+HhAjh-qHzxd=S5+N$!I_o*-HMaLaVRg>G_cJ_$e60CJ~t&K29&LA z)xPGheOQ@5po z`;2kQc4Mxk(he>^e@OT&1r#QZwPfK;!2d%9ULLNziT?0;{ck%?#WvSsgW`u~=N=r$ zA-K%VkL9cmK3&b%zA_dLh2BJ8)nWZBL$P95UdauwO$T|W&{?XcD=r7ckGA44wKH?w znQ3b(gAkhn1!f|GhylqTLDwbP&QZ9_Ic(UV!+M>{Y zDicqU9=e4-mJD82;Y(7BM7z*!R-o%V&SGQP;2GLlZ|{g3brRSmE5p9MUR%>E9CtR~ z7BaBtyd8l!llpb1vK0`A(sArWsH&pt(BD*Y6l8UItsT==t_F~#!4}pJ?XbR+!rXhu zJPCSh+g)fTf>iswh0iEL3!dj| zjvOsyk=$mvt4AI{&2ho2DHdoI(|%L%a!;&hF6p;IMk%f z{`$E5%O5&W#e{j#-YjdOkKR`h!1fT44UGWtn3 z+l*#$a_^eV%-m3><_u>hm3x82OEjJvzVjOM^{=;U0}AdVQzPmPe7PwA=}aHnT(tat z6`q(B*(mdOHdKewi^YWYMEJ>ESrzK`t^{SU{be_0q z$Z3EJC0oC}@=9IixiP<|6UQ;DBruC@WNeO>jA7B}GRd;P>1KgaG)}n3MX8u4^wLq2 zX>}o^^t>3=HIDC|og1K@4G&*aC%gm+vxdLN+S4!6yIzq3yxqnb7_DG z&^PSTkMwZw@QA>)*72(OsB*NafHPGBp8B=w4 zrt{I>LtA}v(?=hmC%rVYm)NgI|K$q@`(5}HJdNE-&7@#2#kmX>HjpBc+2%#b|5 z=~+h(0nc>La_+1{tq%?(7x z%ZWPG#x%4t1Y5GCnh%z7_Dr!(J*&IB_61L^N&kZ*8Cw+CeUqz+zA0*IZ864@Z|Qa5 z!D2jODccuy(2B=&23yQ4ZHoeMxU=zGkP+Ay_vQP&)p$7E>9w;{VWzpr`{wU5dKVZy zi5z4*l{KgTD^m#MQ^Ilz=;W>b-Kss?=OS`2?T(H`Fk_V|PQ0iUBE$TNx?&_XobR$n z)S=BwznD*mrDmFlpbB3`w4j}a;rlwSZG^=+DO^P9${f|4VZ&NX4%khacq^}&h0MJt zynY`&i3>BzjGLBoLSN54X;xIX^tl;UWUQx({mEnz#t+Q|cXbvCoVX8rTP+Cz)H45Hc zlZw_S7Yxf@P>G^TiLvf&P@wAY6Jmu!3NRQW3vbRfgKSwZ4NA(Y*Vb!rK4o})_SOgGlF$eqM5L~yo8gxgYYDl- zPZyNse`A;K6JZ-`cfvpdteMoG3C*p^#+V(`$`-C*b5uY3s*gq49O}1lnYhN;B{XAI zDGy2;j#k?4dNJG^ z7qyP}nb5U3@C1+;=!l5?U3Uw8G_vBb9>~4@8}sw6s{(d{?Ci3;iO*ydpHkbrrBCPd z1*L+|n+z>)W8~-`I;TkZ^!q7Z{Fu}May8IZzZHZxk8XqK^3H1Pu}3VfF=@p#@kh!` zNwhU+U=!+&?OJL(N<<}51-mX$8k%)x_)y(?CP>H{caai%RnHNm|Jro6oMCd| zG;+v=-Zw1I)vw$yb}C*I6?^4@RKmTx{pP9Q$W?55ivIxXkh7v#vXs!lUo(J^fK?VD zCD2T^a#)L#Qm|!uziN{hLAvJH+e3Mkg`UX=U|y@F((Bh^MuJk+76a_}+s`PNo>~l7 zVV6%ogB)mqvg;kQZ;sc^u)d53@96~56ilB(K-3-iYL|DbiG1ch=|h!y0KkJD*>hn7 zq=D7M->-2@dKqIZe4OVi(Sf+SaAL`<`S+|`nzh{ zUMS+4e_O2mAnR7`w5yczTyL~1c0rufP;`>m#!I>fY)tL?%poHp)`u({4BtXE7Ap;D*bcsH6|Qy* zFsiaq#*Xq z!g7R+#@>+`GQK=|&QLO}h>(&3d}dCEa3eOt{yfKID(5H6su3jc08`-Y4CDE=Ri=IO z(%&C8RH8p=<_uYcDUs(w!bK3=JzP5FLb^rAwBw#{!*16^HQw;dkh)p?chX50Tc;TR z#VuNv%ra$v)RH`jF(vk%6+8T3_2qa=1`YtPVL_?|?X?Ly(c>|Kn9LY|NpJIz zg1x!(tqOLmX~~Ej87TtQW}hsJn)`QG&9%Od&tCo6KMEPRCrDb8KMxy_By(V6PL)pt zXw^Q-B7Ds@@v0*1Es7qr54&O~()=AhPyP{U9-0L^92Y%#` z2Sh%>$!h&NifV=LNY$vI#Vg9fbqVFd>);iQ{!q^lLk4*#nP>X$geT97Sj=slG)rgI+rBIKp&x2JV3k#d(z-wqVhbrd zLjL6@7`L~I{Hpdy-CwxP+C+OOFfO*xxHFqx*Yhg?yWh{%AB^hy0~Vn_GX*qLZi@gS zCSwH`omEVg9@wuS4}be+^Xx9WPfPWivl(@QNOU>aO3rbn>;B@bLDT|Rv}a_3ZaSE3 zUOQJOjCBdSL&MA8bX@e1H2gh&BS~t;2it$hYRpiI_YI;vc+n2PD#Q%Zx}4B$;TlC0 z#}1~}sc+hSK_FHjAO*p;;-docG)yf56u_MRXD?W*qrwH~nJ{94Ij2f;G;(_bbT!DM zpmhCQiS@d)he5n}Q<{B^5obg>-u%7D(I4AFq_)PDd)$w`9<5G~KDGHe*7|b+II6TO7c%B*eMhH}a zllHhtkCC~}p$L<|37J(DTA5Cux&9;9!Tc37M{vmOh z6KW-neh$i`j#8v+dsbI%(He3JUU zl6X`%a|6iGlD8p5Vom!ZlyX2OGvJl98v>Y>W{1^|=cn&B%uV`AA=^9OxcT{S`dwVK z95HC`NQ}J~SEv4cwArCN6>KWcn|^?f)??ELB036r(I&vEnYV{U5idy|C`hR*X{o?` zz-n56WJmry0n$WxMvzWNsaVSve?;?Rw_6%$p4*^nuU*y4*TU*m5Fe860&6SElOy-p z<69dayGhyv02$gxNw_^<+eTr{86AQk(z-JBW?)MW(j)!2MH;NR5crv_QdC5AXD^ES zV#MVuNxN6;qqN4{1Ey`{unVtuNx>!T*j*U#yi~h*sU9p0BO{Cnw6~+K>1Ohxhvp{B z*{}ko+3=3G8VGvpjD_Yewdv8EjcxP*Z^JhQA*)v4dYD@&~YWSRA!* zlR)A4{cQF6PF&TD>)Tb-lA288BEFGh0-;Rh&-_A z`T#9&tuX&e%=~(_@%)CkM+bM|-b=%ne?@(53p@qTFksPW)kM2Z$B-$kEItlbZR#yb zHxQ8z`bmTTX})58>*KkUU-=Zjb2MAZ2PPe)@{|3sN8zGI<$y(NTUG7*Gyn7Py^DIM zN4h$iaqE|i?Q;$O%Isq+uv_8YQ>K+K@0pzja_MF3nsW{`x~|GzW57_21rUK2y_cVYcC2ed^gny0?kl!Eaolrd6JBxXGVQA!%T4a zMMNJlni(Z+d7Lr+t(aS#rYVX@%18gHclqL8G+<}e6QO(b!Z58vGA&kadA|m?;b)st zl$r}{20x_fRF4K6QkvYJny2R76EY{irKRffBhA8ck@U0SyCBan4ayTjq|4EY1nv&ufDgzon=B_}kq%lq)Gk(#2h-(q#CM5F1N{ zxc=!pfuZMeUnNh#RG%*BAzv^;Y;458$aezB&Y9JUZ2q-|RwcM9( z$#)Q2WlgG`zWLv%QALHzBZ@_=pH!kyHxe*W&_GJPDt|U@s<(PoF|in&YjKt_J0p+W372W;ijqco@!4q9(KqF(;1^~kkRpb9W5N;6|nCwnr@%USU`!c)>f0B z@k>OhBRwD|m#3P36tj?3$6{E*>dh@nQ2Xan9SPbW-g9$Y+O})sN)|+(+fY)*&awMFO&99YUoO4~Im4i!bNB2zV>sdQTCXM6euQb0g9~~BIx8HeS*Y5{w_m&_%D;r962!asrsAGYY=YQ)+G=!yx4AN@E(^Txmyz~= zs(bTjsQ>tXbdV&WNs)w%K`Lb5DG6hnY{@P{W8e2R$q3QdvR4KnTlQULOO}j%tjW^Y zW$fGUH9p_py?@s#0Wny_$Y6bQt`7q@V zRvP&^2y1csK9RtUyaP-adwx`bx2>JcrzD=az&pHNH*LiPcG+ErF&dEG81xjp@~tXP z1|bmZ&h54W{`Hhu>&Fyt)EzA^W^ycy=SByI zUUDTd$}hwj1dg-AAU2S4Vr+0xLnM{?nUoAn6Y0nqh85*%psU8rtLO9d&*X4X4*_6l zwJvT4E}Okg*{!JPPrbV{YrUO7Je(EVzXYWTK2d!wFTpzu_}wjA%UQw|M2)i2OUxH4 zZj{=;-V`Y*m|Wbrk_$!*MDJ5fR^%S`@Y{$|uG>#vKrA49Gq75G=TE42U0hk%6Ntf} z%Po|-mclB~wGw0P0O4K9W<=Uno(V)L5DS}`7+h^;LPa>C2K?lc@7|fEVFeN4{nu6Z z<;?p#lLlV3_uKynD#Er8HogN$caiQ-KRhD3?J4Uy1)H3}{f{lRT)4VEb4qFjH^J>= z7Fk%2_tbUj)ZkI^h1UqEdXWi4p${&N-BiW(2b)1M-y2W}4a=}eAaP7^t3y^52BEN{`tlW93}!VS1l)5CobpYzI)riT3!30fNLee^{3~HiRr7y>U;ZtF z%`Cng|Db}W@wT>LFpEGJbI&tz5^kgr!UR4A@UfQBb(i)U;0fci-Y$VL?&yG788L6=~vDSit`iEE6|6=XQ ziX+ftn17M6mTvMLa-t7Hy99kkG3=G$|vSTT05?;A5Nxx&4*cVXl$6_E1OrT zYd~j@?^|yW(_VHtJ#1m6uKC{SpC`i&tMz;{XckN4pSGX=P*H4khV`7W3{3i8z>9i< zAK9TLm{DU|eCmeN2e3H3SZ$y+U-wp5APQ0^KX^-YPhlMt8mFt5<@IZWN2~2$UJp#+ zOLmY*?N6f}Bxu!5`q+k3Fd#JQAK|tUqoE+GX`{)21ruVW?;>I-w>4kOv+ufVZ%2W=b&1IF3Ce|!g4325xgqH%wFQ$j#;Nf~ z=MhG5D|(pa;-rl0u?ESwNxUME0q>^BFqUH4byS!y`JzO9rarIgr$MfY@6B}`e@~qp zaa@#w$Cu#OXulsUE+(wIW7y@!hb>o{dl^19tb%$h_KY1@eeld9CC>8-)3e{e-jwtD zEL=^$2wmW^qAwJmaX(+8w_=w}VS2_t=lhsZP(m7o)d$I)JQ^zMAU+N^c7z>#mV^(B z6ndQ-rIoA0X>ng+^{%PLCa({Qp}Fpos|{BFP6oLQcDv$&IfpZgv*iI!bGm3gXB0U#D#hLN+V$4U_Kq-!U`t4A3UcPPW%r zT9neECm`vc7o92Bg{Si;hn<1}zCQ8F@ds;NO9pdk^MlD^X)@%QTF-eL3w(kBn#>Y+ zfeWUYK;$%^db#@@rBsz4fxgXkbj|z8@4pSf?2J992(V~{4fwQQm~i-(z4_A1@z&-h zu30(_TuL*--6p!<3HX$SLqGz-{Cx_Tg`l&^+ENtPNrU)9N1ha$9h8XtLIsWSrh3Bz z+zN<&;hDZ%SB}+@ku|^-L0wP^+cz3%>a(5abCodibe}_jHdeEOt^=m zmn_0eWpU|7Q3CO)XdO%C=ni16_i!5+#({b=Ob*=YGOsWY8Z9DNTFlMk(Nvt5ccT9#9cPyzBxfKi%;9E^5rb^2p)<&(M8Di1I8VpEPI9JO zSD5C2ZVaJ!r^Hxd&|%PxXtcz+kqh1o-T1s~^JMLLqbntLUzF6$KJH7?WzdcrO1R0D zDF0RmL)mE&W(bI|&G(Y}pJ+_)^(aBjV$cPbT*F}~`hRo}Oi=yJ8$j!|=tWC3(k%D_ z^$6+$@818U4{6GGW@dr37g*L+JZwQV*HHJ-e@vD$&{!M&`6QD#@ba{G1n6BbY*TTQ z+L`HkTD(!#BLjX>=_OYB@&*%PZ%D3U3k@giPgFW{BpRf-aixfwhm(m{_*2JxObzF; zk!=#t=wxllqfTaKSp;gAf!gaUNK6}waUzMhgsv`io^OUbP%bB5JSwZq#TI^B2;nob zUpt%fk>~>LOMv-g!z_egZ_avRM>XraDWd5U2f{vDpr*)=<6{r}MN<<8+}rY-tjzsX zIW)Xq9pc zOGqw7lT#@nMiu|5b^Gxx7VXmp3fjFN}1`D5)u0m1& zbuPsvA*ZvA8GuoUE=UG^&YoW-H@yPwH5yG;&w{~v-urAfjcCWEk`H=i8MXC^gv%r) zZtOTdV$MRwsCS%_b^M(Sp&p8Tl*0Tyc%gESR2VoQAU5wF$IWNUjI9L&b8x}vd4VX@ z#YXWuc|F@Z`wu_#Rq%CTo~Q^>2JC(XKwudk@>hM7jTCgGfF?EQStL3RRk_?d zqWBaw204ixZ@ba>K(p`N#!fLnVA`cbYpCKYs=mGfGmCx)YbB++^Qk`Lo5Otbp;*#j z#*1@E{n6QfT7PatvL<>BBJdi0fEgo1P5#9T2QOP}bT^=~2e$$nr_|Abj}_+M2Wm+; z(VB;UOD5^EFSI$4*;O~GswFtw9!X=$7(eYRK{0K?K?uirs%9EL=`Y>9P#4M)@z@&P zwzJKtmo}*qHjL#Bad_&Q(CWJ!{Yx1@PAfgin?a>2NU9ac{W~MW#j*JsU44tg>ybbX zFI`z0g-r2-REu(c7=ISCq(SURS1PlZk?ZF=TWpdl)bK~ zv_V`yy=H1dgEIs#sMbEi^3!MbTBZ%&fXa8WmPY)t%zY9W9P; z2xY9{TLJUy;kixt5Vi>x^O@3wFC$P#!pR&{NmhC5x*rv*MBRDs2Nv7FR;X_|)Y_t( zUdAqagoMU@q1vKIdHV;$#)I1WmK?COvTxvfZ9}X9Y7UqQ3rF_r-0;ht1~Vz&e7<|@ zD$I|PB;uGjqAsrL)vWup4q}v(55^s$PeBux0%X^Ch+>OIvj9zd5#6%3gxjNR@e%{t zkuT09lH^Kt0frv2oT%0;8n*drqvK^)6oojI$8+sf@@Y|4$<%^4Uz@Y6Z!e!XBm96M zz4b@3xZ$fp+w%gSgQE>!|R^Y9fhYd^%DL^6~ z6ua%o$DpHjOQ27232C3wLH}f)t2@%?m&$t_wnR254%pzE?$R7&DE0bhe%X`j&IR?~y_p zl4vsca;ddniBc%h(}YTlwc|--Y(-DDSqvE);DVV@!MCx}1rJ-2XYGwnmS1)~edhn< zkza}t!2`*x^Bi31J6cSUxkfY#w3cFwNTgbaG30t`thByASgSH3m1maj)*m0zz7q&7 zEKEIM%4N^^GZKr5YIu?Lf^W&8zOD-M8-NihtQxz>IO{8{>M#gVdH5(;ugnl~zWFR6 z3=97|Zr6=+|Lj~UP^4@1cG_swq;D<{mtv3@hg%OC?HBUr(nsO*bN5XiY<{?psK{NJ zFqROwd9p^TL%_8-^9pKmGoz9>Ey+cx>qaG|*v&K_VQ0+rMAoGB2&g{8R51LuAjaf2RQIYUAj5)5HEokz(C2dMN z3-BM^&Nm%-olZ8c>{gh)d<%64mUG^k?vHKf>dl?z7Y5&N%KkgUf65kKN9G3cTvu!>Z@~3uoE~MwOdiouYlpFiMYoOT zD|6-(`j13@tEz`B>@+rX>No&eyWqLImecITb>z^aM>{w3vI{TW|8#Ce`QUIvaJoraggim8 zp@`V)f3?gZ`q#IY>P)1XeVos7IFZaY@dNxBLuwtL#lR8B%?)P{nyOK(vsqUr+Fg)P z!byb{*>D$8suq98e|Da{Dz8@YMpRpS{1_@q7LzY%k+H_Cxr znV?P%_166Q83g6)%_mD%@1*SnpK_X2J0_NFns4|cQprfNu5#}bhp@A=#60W!@A@+9 zz&NFrzw24gFVJQYWmmqb?b+YSP(3l}*@cj5yI{cghJDFW!r*2~qHzMkaZo`$3Ac;P z@>#awUE7}CcVxL%?gNhadnQl5;$RNt2R77gL_IQ>0Mp+{QeCqa9kv?py|fV8tw>ne z80Je8kHQJenY^p?x;>ZKl#*I}ucpFNZQ!_sRFdWF-TtKMD8lnUU@bRgRdx3t20bE; zq!aC%<(+LNq1JSfKFb{}*C}=17G?o;_WM1u1h_#yacH|Dmlsss8iiOJ%2@f^8rFHI z+(~0<$g{3^R-ZO#nycm8cMMkc{xI_RZV%tP3P^Re`Q$x#ch;e7_sCn4v5Rqcna$f) z(P-PprcnO=e9dPuIl5Jc0KN9N&Anyqa+eerjPY_8%Yan8yfELN1(1Jiji{CQr|T!H zp|qh}h6EEGeJtbgwC}i^I@yTl8Qr24Gdl1fBJJugT9|BQmF)EEn`ZEdq3 z-}vmlF%Sk#iJCzzyzBz!cju2y zyvzFP4OQgCDJ>RhbaZ)-vOiJur`zqi00RXuP5!!C!~O(XAC8kQ+K;H$xsyFM*;TiT z8(RN0P$!Ewzn$1^MoS-y`j6`LY-qk)ufT z_uvPAooELFnu8#IN{V@+IuGJ5H)n)dIz^syx(X7D#S~O8BeTWf>m54xp@P)$#{-Sq z9Xf=vz@fNu>)%~ZjUP)mIZQ`{3ERM7jpt-8cyl?EJF^C{xzsUB(0EIi0Q5j%m32N1 z+xSz4Fl^J> za-;kK(BOcwG%mnV_6hxN)%*9E_0KQ6Xc&b450;j81YGT2T+fF1#Z;M)Kn$_y5>S2+ zX`o9j=29ousrf1@tfq0GSf=x+`s73PiqiO0+T-9I}$941T}&44L)Wsq&~U36W40$^PUx~{Z??~Aez@#Xwc zGGu9QV|_}nqQFF!MOZc$T>99HKKGy9`;q^RjDw+s(+#Mg#-7|De+IBXYBUtA&3xsv zZl7-ll(4vWUt#`i^H=H`x33|yQRUW$Y5xrvnX3U9g=J-Qj+MIG7?8RbvmA|Xis9xc^V}d7yzJtEKcj=g zR?h}vE;fj+r@R~=UFK@$r>R3$CDun zpj^Bym7ZV(?5r9H`yotz`j5dem$k!hrGJ`r&fCx}=^$z>n{BuXGveiOzD99Dj2(Dm z&ZPxguok|XS~4a`;Vao#{1VD~aA;CipQ(q&m9d|GX8dK>5n$YmP#cOQc2SoUm;41Y6A~);aL*!ZJFzc2cjf-k4B;P;psoE#2BFNjSjo>Z+}2H| zxDOF~oXWTmQfzQn#i?Zg@F*qSJdF#0yA+tnSgAk!Jdvx$AY@nUCzUzhm4;!1q1wMC zf0s)9XE&I*J%wRQdM7Ugmj&tL-Dula@{ml;PO(%df`kr{R>7;{yjSeLj4I>aVKul` zZlX3jSy0Ke-L3tuL`L^19f?ZM4R-^Dc}MEx08sy?gIQ}#9=H{tf@J;@r4gdRPHK3L z(&(PJa?y2J5N))Uy9i|tXHTVl7|FC&6cMrW-1VPvP7e3Gl-lIHyxZVUVqV^Xh@?*i zH;;T?L_~nG`e=!W;KmsZb^m)hI!%9NZN!B{w{GX;6j|CX%4w(@gO6gKTg?36&#}LX zQD9&|4!5)@qa}PPr#!2GM9DdA&y_F|O*g4dFw#3COSm_o@`i zZP5c_-?sn^w#;@Lg(4+KXeh8JZ7UG>c~vt zVYy#r$11+(`ui8i1k&!SLxR2!4CGuXK6CtAEPmckJANw?8O-TvHFToz0u}@!tm&9ho%Q1|tV>Q#BiXXf&3_!~ z8tGiX9Q|j{7)ylYUp@Ht==REQrA3)Ku11bu+O1_Oo8}=o@wvnloZz1 zN%boaM;Xy~5u5K)_AhmJg}HRf z+87CnZVl4i@hPE;Ex)Ft!*j7zmg__|&hx45Vp-^C!X0gyhb?@~g1S?o?C4P^3vaMZi}RHW(|fZ!*JAOU%@0-Z}edn`}(`6_RX$nUg8m#E}0LCd>bqya~bNnm=c($vvl5 zBC+6LuZ^Dxnd)Kw!JHu1oqIeq$=j+!rNrH+TH`aM<`+OUd|aQLmiea>#C4Q$E{nU# zk1%X^KtaP3a->7ep4z)TO&x2l3!bJuL=kljQL}5$oe#oHlOg`*RHns&6?vRR~+Ylr@OX9UweD1DsY@kMgXRY+!19_aa#Ow91tk1 zuHHO5n`vgt!<}MwP$m}_7sIk`=-`gPU-fo1o05?|T3E0pEZ76M)$K8#@b(%20#+ne z*)-2wxt=$2(q!YOVmS9o+e9D;B53=+u>cvi#eTJLjxyVg%TO<}Bp$`yp*%l`6S9*P zOqi-3eYo;7$>xGLS+{Ou8P}0U;oe^o`m+S96G9W5go|3Qbhzg2qj*@UFlHTu+L&1h z@zCE3P&=PH7Y$L@*gCP0b}}8~tRJF`i>vEDJo(y*&~Rxyj5=0+pOXWWnuj_%N9ppC zf@CyI{3kn%e2Z1V*KBMTBMSC&Fs=O+%?*^B?~gnB!>E(3dhWeT&V)Z(O^ucO#BlTE z0=Z17m)IlVGiDwTSk5@(Pgi{(=LK)L<5ydVjfOX<;HX8ioXp5GGDfZ0k40gP`g_;9 zu5Hdcj1KP<=1VN@^y3MH?)?L$R6W?fU(e&M>xG|6YJ>a&d*(l)3-|XMDGS~aOY^5r z%Jk%HT3_d)FLpm3;ToJ2nTcuc4zu)HY975R5A@K2?H+fi3w>)t` z(UXXg`rp@koeN(m@=8@IOuQ*L6Cqlc+tl0GLO7;yhJ z5lk+d$>;d|UnlQEd1rQOBnd0FM4!<100fo({Y%Z6Z=S~jW`JEVHj#bLo2=pTDka6m93iV%yM&)$f zx>*&$d?72rh0lXla5oz1>MM7CzctV&?m+Dn^O(l)EN2Z^9g5e8ZemP`ol617e|dO* zZ(q4?BQ@#uqpY(HI5ROr|l|zv4EdasMz|%GM+?-7B&CT$_dXk93H!R6E z{oqF>b^@JH4n#PpzP69&&3W;j_vJoIAUo|81zpkM?>D1Ih$`aNm(u*TC^u{1AX>fFm~av(k0ouHd|$(RXEqVL zTeh7N{vHXDeTK1u9&5>%#|fwBmbUA#*v5{9G+ca#SFcFTbp^e z=z)V%LazY`fh^o3Am@ODTHpfug7!+Dio9lrak}+{nnOEjd8ulq|=LB5lRdnJ>;M{lrGt-HIos9wBoY*ub5 zY#!ti&NBvrs{&Zf?T+V`O#zQiA`epS2mr>&J>P{JM@a2M#%5`taNmJ3_NBEFzk(>Q zTEOcZ=h|GbHH8X6Wi*NP%{`eXNoGfknz#h$TNm93592k8y8ivc`2%^&P#GI%O+})( z#xLsxbak|~v-Kd@no!!5;;rFuT59_=|J+o=krAsbGCr#;f$I%6+e-5&cN9p2C~eqbDRKIwvG33jLk)+s;6(lkfh` zv&qz#HWrHScctzmd_4~CzJ)Vq*QGm1d-yE<&-1LOB@6aHs1%NvG}MS&X&^j!W-E8~ zNQ9$oyG8^urP5&>oi?$0_7YV5*X2jdLy$M>o&4OeHy=mITn77Z*;_G^gHQwgaO1_Y zz*gG=6P4p@9V~#{vVJ%`32!~zlZV^*e9uJa3pq_L=!}&(valQobvrgB2X>A;F$+D< zpRpeYU`Njon+&d0E>f|A7mo9R$JIY}zE%9Y8(dG|l74no#r~g=NL#a?=vs1bZN+E) zzswWuY!@eHk66w@l@(vgrzJ08tNMCV;-5aO|DL+p<z<3cZOM*b~S9--L1IVbL( z-2LV(8!MD)2rTYt%VL3<=H#B!l=)=+=e__I4#A*f1I5|Tl!heUfJm4JGKub{WIN>GJn@wgGzlXy;^<}T;yASRL z6frS3BcfuOvYm6Hziez&=^TGbx3sWdrU*R@`MmN)4LOmD?D>!z`qxQ0`KydH&@tr6 z95p%uT$n0}tAzpkx)uz*Z;Ag}4nM+XbW_sV8q)Xv1<-qYEBY4#I!?H>xzH4b7Voq7 z7t)!svA1tRccjui=j4F_7}C$TlR!WH85PYs`=G-C1r_`;GU9e~$J9&2(z2MDNN;t8 zq?j^E`i32@~0$TnlCDZyRs-w`2S8s1>ACGI3>2Y^5# zqFZ7bnv?H=*|g!239LJQ)KC1dW7Y$lRLVPV@DHb@P02cC1_t){asFake5<6!LdUlab0Tb_|>SDtL&uxbxLV0ku{%s}yon^*k`g`~X2~ zhd7a#A3u1=d$}xZV3kVva|F*6inSo>!*`{nI-D~-ok@0_syKbR1BqL z&?=6J7w&>kd29P+V#=DA7#?2ubzngFdE$5b@bW9rC8!Gxu3Lo(!Xqpf_*n5-I+>Ik z=8f8JeD&g48jNYuuUrHob!b_?*lA7uZz??9u+B+M1<#Wgqb7~@l($j9)nX=Z5^5P& z^=Q3RrL=L36>Alwa8yPYuO2hYon_z#K(FyUd!rR7Vt2J25=UKwOe*(NSZ#*QJe$VO zTFY?|w0<~XBhYoQ8I=^Rj_)B;j~lVt$B=sF5h2**Uk?r7s2Bo80M&K}cK^{4mu6lD zCQN#|+{84-{Srs)TdXwlaS)zj7`e9GB9`C|dUeHIp9~l(im?Kpm@AmMc2xFXV<~1u zPrLEJ6lr*K_&U$Y9T_QPGOFRLt($xU4hmj2B5*IUD*zcsjaUQFIUK0Fpt;hR2uIN( zdP&(^Gyoa*{RwvdL2%M13ZpsF;|p6FI6-Ow<#7Gg{tW5>Ol|4XWHojvZhtttFLy9r z_(k>SIC%8%s?%9fyR2hF91oYt;I{_jT_4&33aPeZCiz1q>6W;wGGsDZ>?!WOa%y3Dlz$I%WI>{~LAHdv`$jf=8~> zdI;Z(B~a|iHt?LMK;Xs zfSYjhhNapfD8Bvr<#=PBq~&kJ8-Kb!0=(}u34`U!-n~6ODo1yCWH0bOagc=)&o)xK zQ$Yz{IDwYP1qHSZ&Gm*~75FGgTz#DKVEI_l?bE_qNWsTZg;C($vG>Ff$6lhOdJD#L zZMcaD%%oYFnVO1}R9;XV#xsASiEBW8st%r4{Bz%VmdytMqHzft8KRH7-ki3=hdHhY^8_Px+3V(w&R3Tk*)lO> z+K{sYvww+ult;wmBW%O7iugeU};ML zZW8~iiObZWE=Db`ywP=Np|C?7UT9i3MQStDR4I3K0uW6h`4S!iApQ%Yc?)mL;->ET zN`G$U+M3c%QnAw#{UJM;_dF`KZK3$Z^>C!;SB;nt!n?X{)ia!??O+Bh}f?zUI z!Ln+RtoT}g>N$tOMp%_9zRt4=_*uK`=kUhmnLr_=#5`8(U~ORn&Q7eO{jGJA=@;1L z!H}%M58a%;ru1p!7Je+`^07pEzDiPlg0%@3svV15Imvfz5=$t*ndhWOPhqNnD~sE& zYFV<~aWTLQ#ku>qHYF`YF8(qE#n=JhOc7ViU@F!$84}2vL^`2j&#A;%_dsoSE&?4Q zb$Ip97r3kp~>3WTt+4|KEY)~gtsTcx3>P2OsvYO%=0hrC##$wyI zIf!STo(mR8b$F$uWG<(Qs^=}y)5A$Mi?o9k_i^9j)9MD=0#*;$^%#YNSwl&FV_9>H#Ms*Vb_7>9moPHO4)u#lkD4m>oIE>1sI?SWHyjl3Sl>-Q-0EI*>l zjCS`m*5lIHRYS!?@pVJ<{241yQI^`6i{OzT^rW1$RIAwXh)#jVAj_3NG8JEi-+_E9 zh6uZ%RnA~+JFio`Lxhv+YkYtS6ww>!ZuU?1@9-KNlz`Ix23C7VBjGK8f-_6hI4?T^ z4>=bM@Fn%vQ|GeFU6(f`fazNoV+{kFo0_qMYU?yRx@14UW z24l_gx)O zKx8HntMNOee6cQT6T1Z{W7)p%2}kgN(m{U@;0`mSXR3oD+5f1-g~FpLVN`3ii7Hx*kbs$M?|KY zVS%Taz7p;gM$_@P=>znf4y=O!r&wD(tS}kDfpg@~Ka!9?$!G z%-%IXq^j0(l@_b{tFew21w4dD-`=S&*3N;a^AXtLDJLYfX(>Bf7%>p5}gMa!-TRqOUmfm&<4kwe|4xFGHxiA8$rrxCm$k8#V zi(mR}5?FsUhoQCq5@wrXl2RsO&TLF!`pNVI01-|d7Q2Ip`QSs{T|3NJk$tiXnxsg&x2o!^Y1W(;Z%`rmL>mS<+~xZKj2mnvXb(F5$(N z101OB`wo29wVUMP=Womfj#D18te2vnh%9@MHvNA4>s+pZ<hvN z_Y$n$zWOIf{FgK8;F>| zTh~oq{!Yp|Vp@OQa(F)gB2f_LIYb$F6II2CPbo!LpYgu1E`N{b{Dh=6PfA4s>0&(C z=+IGvJKQVNeD(*+?MUgl`0Yg?M0DYt$vx>*E>Mp73be`oLm>< ze0~5Fv=#G}`8R->d}F2z&{oj(uPukbTB2=M3yxq+89M{&UOE5_92zab;C*@B<=B-q zcJ5#S&twi-X8qtq@@cy@FNHN18EE|aOeV$08)S}|JmX)UJzqqX!3<5R7%+um*few$ zSG0h8;T)NfQcI?-8F-5$Ihrv>rs5_nCpf6fo_x0&8(8I{IV>GRs7_4ryXc&PRjve0 zp)O?I+lR#zjs^abZucR6!;R-ifiGG948tKKyN#n3Zcp;b^mp=6!@sUq;$LY5XR6@K zvbSHO(FC-_Yirtp@Q}A-n4lJWlLK;ai|vWk-uZv{W{W6&bFuB*^YkDlNSQ%DKB*nI zL?(|Reon;S=7u>&qJz$OLK@M-53EK@wkb)OFMnM&bnma-8$-gy1nK;>a48WE7qC?( z2O%Qi`;|}mrpGX#aDe6&=&(`?hX|*ZGDTd10IIU4;{R;j>xZfw>G{IB?i8^{v9xm6 z>*h+jOZm0+D~MvpyoMQodPhrK7@^!HFR5O=(tWL|Ab{-jaO%fQA7bb}_;BiIuxb@D@1=vR&ond%)?x=@zW@73w-8ycPo3ZQ#cH;T#Pf27;`i*Htsb|@S$Ji!M z`DHXVNT)GraCHGBu-qoRVBBx~Oapf?tvkskX{a>fS>q~{j0esUsN zZ=m?5g{CPU{FLBHv4C@hmgz}UakFh@3u30@Z z{T{u(y+&H?WhNvm2P(*wzL&E4%m^opk-JLxBB&B?1EII8Av|pU4c({#9VDdiV*JQ@ z$+ypXS~(s~Fs*Y;XIg*v1*Khug(=_xSz#!(%%X|jxwqvB*r|$NCv$t%Ddd)#vBY}_ zfwx3nM3PEMzWdbJIw!(!pmBjTuyj{8GE*AaEtU2&m==>NB>{V*;RsHN?R*-wz1D}l z&0r?rZMuRlQUw)_*qSXvCPI|S>8*&z0oAO#dTUGm(^Sxn_Sz)UK9v3V)NsAq7h_82 zFIbc87tEvBV)bL?f;#R^GohK{wnz}mdMA3WybwiUN3n@IUpN?C0ZJ=~hKN2tzZ+pXyV7jZJfU{v zd)t|FU3vNySCdjpH-QxZbOHMUk8~i!D@)jB)e*@HYP1@-Axx3&Z??}J*%;5_0Co~# zblG1SxbxTPJCE&-$3wUSH{na->i2(l;)$M!P1`9N(jkD9D8!U~p33eRNu3at{%$gk z3wZJtT`H8T9yZnRCLJ)|3Os}K6@l*6^Zd8m=Pq&tsC#I75C|?UxVEk; zQulD~G_7A9FrIl6Q&--Wzrr#YR?`=_hG3P4{!@&sE3bnD1?{Kn%0LZVU3;x^O~)}qb$pvSv{MqKEyE$^!PkEk}|+$*-kN? zE}a6-Isq{G&sL6$UV|rjO!{VI4H9?}W*A}#eutD_*=4U8SDu57G`w?m(x|St0EVJK z@Kkss_XYEE(D)DAB=8lGLuGJ>EbFotmGu>#nfebw;?$bHxESUM9*Y$Ku#go$5~*2> z@gVRzxQ~hFgHed^!Bsc;fwV{uWwjlvM>YjDjh_NCPnSM_8R7JNqcunsr)IlT!;bNM zODteq>wXWkSyhsDsZZ+4+U*s#w|p1Z^aST-NBU~$v0JP|(!?!-Jlw(PAdqw#$$+dM zClUJ@&34z~%FR{+chtJi^)~QLWHTwjWy8IZ| zgW*SQ9Cx*tVB`b{%!|8{MyI24A~ZsrjUom1XyCGio=pc_s75U3z;dAYEl|A|>%V@g zq#GDuwzY)|i}<|M@MWG&R2&3eOyKa0?5#N%DUheP0^TeIo1yRXbJjqq`;B)yZnAgV zaXK05VydG4e!;K1PV*oBT%jnl7aHE}$Kff3-jEPTa*EO;zNb^kp!B5_Usl^<7t5$*#4($>+aL9u-spiEAw9iiNAZcM0#BYZ>07Z z*KcbU3?@2y({)1xJgj=9SvWZL(hL&Ov79#9CesV%bhH|7&hwq1Z%^o!_K5Kp-4`Pt^b*Vt;|1>@P{qgAO*27 z3??tBPs0<8uD%O1J!?rK1f2~HE=-)P%n&3gu$#RK>qX^Dy4w@%%i|&rVTSSeBpLQ z%y~os0&ursE7%-lrP zAW*^rm~C~Z+w;%SuhJO6-|4WD42xfz@ggiYjA_%(q=6WzZe6p167D-F>)z<8YQOjk zy=!mL2pR!vLVDnHjBgA!k|suqRlm;Z^eS5OlAg|t#%K=Z=V}*qdCd}hPya8*q=MH4 zrO_L6j;j*QZh(rrZMJ4037*u?5=dI}{Q@54t2LL*(oRD7W@5W-%}CWMM(uyoa&}ph zAqpsrC@h;n%pABiPnp(#gZ=;YcNYJDgb5aZRzvfcX?@x1cvBB}|35$d*Jcxl_-|e5 z<-F^^nsx=CG~u+V;{64uJY6`I##rWb-;%e!lAyfc$AJC+d;c0eaavoow69r`dK1(r zY?G9@NU2KZaGO3_rb6_cf|eTkNZ%ry;$ za)mP@kMlI_Er}oF!zZ!y@ADkqcQk~R4{JZ)>GGQY&hF>9I=PxQF*Pdf)-x2)goztZ zUl|WrbISSQzb0uz2Z7upd&L8406=awub(0PrM#K}fmnx?-+(~QTOkzh>p~#!o1YDm zK_KQQrAHPX6A6p8Nw;UzN0qr)hn~6mDXQ!L(&63o$j|?26;y}SZ3)fK4+6Gr&5S*JKNE~ARuaAlXtx-AVSn6`p1(8(n~AtWe3>#|k-x+q z51xIvJwszV7W8IotL}7QY~|6@(WF~5l3&<$S|;+Bz(WxQ1<%PLkP$EY%!RHdPwOT1 z9ht`2uBwqY*`ijab2&9$nlb4srr?CJewzjO8;jP)8m?kBi{0?mOjl?{G_CJ@Lps;8fj-6K=` zJ#2iaU88bX$;mclD#PUY+R$dTaF7L&?O+c?yqu%uPzPg5PMwDvtY>RSDeCL9xv{Gg}4;dyk*yy=(#a~tEm|yRz zjcr}kSgiF@-8bc{RGP{yIv5mmwMW7yZW5xxQkLmQj^Ajdi1mh76&$`p;3RFwSla%+ zg;%T`a5bo;+Uf2$78j)N<$xxce1x+OMOuUk?`(8aNx=M74cjrZc=uLvS?!jv&g)0X zT}Q8eQ$vC&zyTD%O)9K(_QIT6oM_qLh~%c6?hodD8Ty|bsG9y(G;kk?R~}<2J&(7v zG6HPJ#Cc~D{GR)^*$0F(ZLAiW@s%v-m2EmdD&ufjQk!ICrfZX|s#|ryWR!)=_Ua8c z`20;9Jz(1L9ETN^HY82Wp(07+hVswwEZ8S4`fh4@ZeUq-Ek1*f8Oiuvhyv728m()iQ1O>;TltUd9hJDQTOS4W2~z|MHN}X;juz8bT zg#{>6v1dKY0hh<7C>UH8DaUo1kY2jxqr+%X<|8c42Cjo+ut_gB)s_Q z^!k^`6ff#PLot7G;MHE}*Y);pxpX(_c*#STCA(Yljs?cdr%>EAXW7C@IrteIBLp#p7Rj4)9n^~oV z`(+15n2lCu`$yMK9wC*76AJt49W1Yl*DX_+EU=3E{rV$z5KL%)9dzvZ(ptNM!lse3)dZXV$M>=SP1Qb zClt+`t5__B_SUb;Vz9M=Wmo}$l6KwgLFmRVXyej^t>UZh&lOFM+Dul(=;}4`5Z;7s zN9=ZMW$?vchDI-ECK975J^g@qa)Xxc2J7I;E4Ky8=(9~*ly&v|_LP|*kl}dKK5wKp=gFOdQdX z++3It*#Tv9ldHU?0=mEWT5Es>?@ONCTSuqPOO1V>)DEo5$?<6rcJi_w6L$9>N|vwa z%IFs*u9r85OMop3Z2@w~?Zzy)IM$L!|YXIC<`{qN3Op zAbxNurAL1(unJpNv4_hC5qy|H7E5iecE9n*{MvikdT*}0kxlfd>KvQfo1(8J z^l_H?OTEf10R@Z6q7CW}2j>yJn4=QzIHXcb*6n43%)&lOjM#t&y{*HFFto3zgg|LK zrUsWq;Zhl=&*f9@1PLZ5 zT|WrqQ82gw;OG2Zz$O1LG$o)NK}e(uf&9PN_}ynI(hML<|8~Bx%UG$kln!D~|M7qG z7hU-i5Af+odg?sAuAY3!A{|7cgS!A|hY(5_f9%nso(-h={^P>`M#-QfsfhniEqs^z XXubV?$QKNP)FUnsharing failed Updating share failed Use Android device protection - Use android internal device protection, e.g. pattern, password, pin or fingerprint. No need to re-setup it again. + Use anything like a pattern, password, pin or your fingerprint to keep your data safe. From 2403ed5efea7e335456a95b902bbe5f987daf99b Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Fri, 18 May 2018 09:25:22 +0200 Subject: [PATCH 18/26] changes due to merge Signed-off-by: tobiasKaminsky --- .../android/authentication/PassCodeManager.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index f3eb3b9dd8..d3c88aefc7 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -1,4 +1,4 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco @@ -84,7 +84,7 @@ public class PassCodeManager { activity.startActivityForResult(i, PASSCODE_ACTIVITY); } - if (!sExemptOfPasscodeActivites.contains(activity.getClass()) && + if (!exemptOfPasscodeActivities.contains(activity.getClass()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && deviceCredentialsShouldBeRequested(activity) && !DeviceCredentialUtils.tryEncrypt(activity)) { Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class); @@ -112,7 +112,10 @@ public class PassCodeManager { } private boolean passCodeShouldBeRequested() { - return (passCodeIsEnabled() && hasAuthenticationTimeoutExpired()); + if ((System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { + return passCodeIsEnabled(); + } + return false; } private boolean passCodeIsEnabled() { @@ -121,7 +124,7 @@ public class PassCodeManager { } private boolean deviceCredentialsShouldBeRequested(Activity activity) { - if ((System.currentTimeMillis() - mTimestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { + if ((System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { return deviceCredentialsAreEnabled(activity); } return false; From 28c2967739dfebb7b2a9bae7e5806585c3dce86c Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Fri, 18 May 2018 09:27:43 +0200 Subject: [PATCH 19/26] adjust what's new to 3.2.0 Signed-off-by: tobiasKaminsky --- .../java/com/owncloud/android/features/FeatureList.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/features/FeatureList.java b/src/main/java/com/owncloud/android/features/FeatureList.java index f458dc1db1..728c3d1343 100644 --- a/src/main/java/com/owncloud/android/features/FeatureList.java +++ b/src/main/java/com/owncloud/android/features/FeatureList.java @@ -41,7 +41,7 @@ public class FeatureList { private static final int VERSION_1_0_0 = 10000099; private static final int VERSION_3_0_0 = 30000099; - private static final int VERSION_3_1_0 = 30010000; + private static final int VERSION_3_2_0 = 30020000; private static final int BETA_VERSION_0 = 0; static public ArrayList get(boolean isMultiAccount) { @@ -70,10 +70,10 @@ public class FeatureList { R.string.whats_new_ipv6_content, VERSION_3_0_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); - // 3.1.0 + // 3.2.0 featuresList.add(new FeatureItem(R.drawable.whats_new_device_credentials, R.string.whats_new_device_credentials_title, R.string.whats_new_device_credentials_content, - VERSION_3_1_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); + VERSION_3_2_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); return featuresList; } From 19899b0519ed4cc678f9c8b9d54b4999954c8999 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Fri, 18 May 2018 10:04:23 +0200 Subject: [PATCH 20/26] do not show document content if protection is enabled Signed-off-by: tobiasKaminsky --- .../android/providers/DocumentsStorageProvider.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java index d89e31945f..49507e34f9 100644 --- a/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java +++ b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java @@ -26,6 +26,7 @@ import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.graphics.Point; @@ -34,6 +35,7 @@ import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.preference.PreferenceManager; import android.provider.DocumentsProvider; import android.util.Log; import android.widget.Toast; @@ -47,6 +49,7 @@ import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.ui.activity.ConflictsResolveActivity; +import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.utils.FileStorageUtils; import org.nextcloud.providers.cursors.FileCursor; @@ -68,6 +71,13 @@ public class DocumentsStorageProvider extends DocumentsProvider { @Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { + + SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext()); + if (appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_PASSCODE) || + appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_DEVICE_CREDENTIALS)) { + return new FileCursor(new String[]{}); + } + initiateStorageMap(); final RootCursor result = new RootCursor(projection); From 10dabe7145ddf277a342f092a0eb1c2f46926a22 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 30 May 2018 00:43:12 +0200 Subject: [PATCH 21/26] cleanup after rebase (removed unused methods, simplified statements) --- .../authentication/PassCodeManager.java | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index d3c88aefc7..86e1e5302b 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -44,7 +44,7 @@ public class PassCodeManager { public static final int PASSCODE_ACTIVITY = 9999; static { - exemptOfPasscodeActivities = new HashSet(); + exemptOfPasscodeActivities = new HashSet<>(); exemptOfPasscodeActivities.add(PassCodeActivity.class); exemptOfPasscodeActivities.add(RequestCredentialsActivity.class); // other activities may be exempted, if needed @@ -65,7 +65,7 @@ public class PassCodeManager { return passCodeManagerInstance; } - protected PassCodeManager() {} + private PassCodeManager() {} public void onActivityCreated(Activity activity) { if (passCodeIsEnabled() || deviceCredentialsAreEnabled(activity)) { @@ -112,10 +112,8 @@ public class PassCodeManager { } private boolean passCodeShouldBeRequested() { - if ((System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { - return passCodeIsEnabled(); - } - return false; + return (System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && + visibleActivitiesCounter <= 0 && passCodeIsEnabled(); } private boolean passCodeIsEnabled() { @@ -124,20 +122,8 @@ public class PassCodeManager { } private boolean deviceCredentialsShouldBeRequested(Activity activity) { - if ((System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0) { - return deviceCredentialsAreEnabled(activity); - } - return false; - } - - private boolean hasAuthenticationTimeoutExpired() { - return (System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0; - } - - private boolean fingerprintIsEnabled() { - SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext()); - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false); + return (System.currentTimeMillis() - timestamp) > PASS_CODE_TIMEOUT && visibleActivitiesCounter <= 0 && + deviceCredentialsAreEnabled(activity); } private boolean deviceCredentialsAreEnabled(Activity activity) { From 077f48606921e837a867d6149c29c3b7dd3383ea Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 30 May 2018 00:47:16 +0200 Subject: [PATCH 22/26] fixes after rebase --- .../java/com/owncloud/android/ui/activity/Preferences.java | 3 +-- .../android/ui/activity/RequestCredentialsActivity.java | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index f80a168403..b1fdef81ac 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -892,8 +892,7 @@ public class Preferences extends PreferenceActivity } } } - - @NonNull + @Override @NonNull public MenuInflater getMenuInflater() { diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index 456119bf5c..2797521682 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -30,7 +30,6 @@ import android.widget.Toast; import com.owncloud.android.R; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.utils.AnalyticsUtils; import com.owncloud.android.utils.DeviceCredentialUtils; import com.owncloud.android.utils.DisplayUtils; @@ -66,7 +65,6 @@ public class RequestCredentialsActivity extends Activity { @Override protected void onResume() { super.onResume(); - AnalyticsUtils.setCurrentScreenName(this, SCREEN_NAME, TAG); if (DeviceCredentialUtils.areCredentialsAvailable(this)) { DeviceCredentialUtils.createKey(getApplicationContext()); From 20bc9d21af5990ddb49c7d24ec251da5652ca4af Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 30 May 2018 00:56:19 +0200 Subject: [PATCH 23/26] remove unused variable --- .../owncloud/android/ui/activity/RequestCredentialsActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java index 2797521682..26adb038f1 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/RequestCredentialsActivity.java @@ -40,7 +40,6 @@ import com.owncloud.android.utils.DisplayUtils; public class RequestCredentialsActivity extends Activity { private static final String TAG = RequestCredentialsActivity.class.getSimpleName(); - private static final String SCREEN_NAME = "Device credentials"; public final static String KEY_CHECK_RESULT = "KEY_CHECK_RESULT"; public final static int KEY_CHECK_RESULT_TRUE = 1; From 7e338b8ce981b135ae116dd29b9098e97cc3d211 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 30 May 2018 01:03:24 +0200 Subject: [PATCH 24/26] lint: remove unused dimens value --- src/main/res/values/dims.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/res/values/dims.xml b/src/main/res/values/dims.xml index 517be6899d..7a41e77ae5 100644 --- a/src/main/res/values/dims.xml +++ b/src/main/res/values/dims.xml @@ -112,7 +112,6 @@ 16dp 24dp 24dp - 32sp 16dp 16dp 16dp From f4438afc033f27ff6d9c5746fc0f5cb24f695baf Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 8 Jun 2018 11:05:21 +0200 Subject: [PATCH 25/26] move preference access to manager class --- .../authentication/PassCodeManager.java | 13 +++++------- .../android/db/PreferenceManager.java | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java index 86e1e5302b..bf2270f86e 100644 --- a/src/main/java/com/owncloud/android/authentication/PassCodeManager.java +++ b/src/main/java/com/owncloud/android/authentication/PassCodeManager.java @@ -22,13 +22,12 @@ package com.owncloud.android.authentication; import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Build; import android.os.PowerManager; -import android.preference.PreferenceManager; import android.view.WindowManager; import com.owncloud.android.MainApp; +import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.ui.activity.PassCodeActivity; import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.ui.activity.RequestCredentialsActivity; @@ -117,8 +116,7 @@ public class PassCodeManager { } private boolean passCodeIsEnabled() { - SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext()); - return appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_PASSCODE); + return PreferenceManager.getLockPreference(MainApp.getAppContext()).equals(Preferences.LOCK_PASSCODE); } private boolean deviceCredentialsShouldBeRequested(Activity activity) { @@ -127,10 +125,9 @@ public class PassCodeManager { } private boolean deviceCredentialsAreEnabled(Activity activity) { - SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(activity); - return appPrefs.getString(Preferences.PREFERENCE_LOCK, "").equals(Preferences.LOCK_DEVICE_CREDENTIALS) || - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - (appPrefs.getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false) + return PreferenceManager.getLockPreference(MainApp.getAppContext()).equals(Preferences.LOCK_DEVICE_CREDENTIALS) + || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + (PreferenceManager.isUseFingerprint(MainApp.getAppContext()) && DeviceCredentialUtils.areCredentialsAvailable(activity)); } } diff --git a/src/main/java/com/owncloud/android/db/PreferenceManager.java b/src/main/java/com/owncloud/android/db/PreferenceManager.java index 185d448f93..e5ae305628 100644 --- a/src/main/java/com/owncloud/android/db/PreferenceManager.java +++ b/src/main/java/com/owncloud/android/db/PreferenceManager.java @@ -28,6 +28,7 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.ui.activity.ComponentsGetter; +import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.utils.FileSortOrder; import static com.owncloud.android.ui.fragment.OCFileListFragment.FOLDER_LAYOUT_LIST; @@ -181,6 +182,26 @@ public abstract class PreferenceManager { saveStringPreference(context, AUTO_PREF__LAST_UPLOAD_PATH, path); } + /** + * Gets the lock preference configured by the user. + * + * @param context Caller {@link Context}, used to access to shared preferences manager. + * @return lock lock preference value. + */ + public static String getLockPreference(Context context) { + return getDefaultSharedPreferences(context).getString(Preferences.PREFERENCE_LOCK, ""); + } + + /** + * Gets the lock via fingerprint preference configured by the user. + * + * @param context Caller {@link Context}, used to access to shared preferences manager. + * @return useFingerprint is lock via fingerprint preference. + */ + public static boolean isUseFingerprint(Context context) { + return getDefaultSharedPreferences(context).getBoolean(Preferences.PREFERENCE_USE_FINGERPRINT, false); + } + /** * Get preferred folder display type. * From 6ba8b24c40615f9a74619c567a1f08953af7b59f Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 8 Jun 2018 11:08:07 +0200 Subject: [PATCH 26/26] fix feature list --- .../java/com/owncloud/android/features/FeatureList.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/features/FeatureList.java b/src/main/java/com/owncloud/android/features/FeatureList.java index 728c3d1343..414aee5922 100644 --- a/src/main/java/com/owncloud/android/features/FeatureList.java +++ b/src/main/java/com/owncloud/android/features/FeatureList.java @@ -41,7 +41,7 @@ public class FeatureList { private static final int VERSION_1_0_0 = 10000099; private static final int VERSION_3_0_0 = 30000099; - private static final int VERSION_3_2_0 = 30020000; + private static final int VERSION_3_3_0 = 30030099; private static final int BETA_VERSION_0 = 0; static public ArrayList get(boolean isMultiAccount) { @@ -70,10 +70,10 @@ public class FeatureList { R.string.whats_new_ipv6_content, VERSION_3_0_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); - // 3.2.0 + // 3.3.0 featuresList.add(new FeatureItem(R.drawable.whats_new_device_credentials, R.string.whats_new_device_credentials_title, R.string.whats_new_device_credentials_content, - VERSION_3_2_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); + VERSION_3_3_0, BETA_VERSION_0, SHOW_ON_UPGRADE, false, false)); return featuresList; }