mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Implement parts of screen lock functionality
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
469483d67d
commit
67bea68a1d
@ -21,14 +21,20 @@
|
||||
package com.nextcloud.talk.activities;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.SslErrorHandler;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.events.CertificateEvent;
|
||||
import com.nextcloud.talk.utils.SecurityUtils;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
import com.nextcloud.talk.utils.ssl.MagicTrustManager;
|
||||
import com.yarolegovich.lovelydialog.LovelyStandardDialog;
|
||||
@ -51,6 +57,7 @@ import autodagger.AutoInjector;
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class BaseActivity extends AppCompatActivity {
|
||||
private static final String TAG = "BaseActivity";
|
||||
private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 112;
|
||||
|
||||
@Inject
|
||||
EventBus eventBus;
|
||||
@ -58,12 +65,62 @@ public class BaseActivity extends AppCompatActivity {
|
||||
@Inject
|
||||
AppPreferences appPreferences;
|
||||
|
||||
private KeyguardManager keyguardManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
if (appPreferences.getIsScreenLocked()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
SecurityUtils.createKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (appPreferences.getIsScreenSecured()) {
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
checkIfWeAreSecure();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
private void checkIfWeAreSecure() {
|
||||
keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
|
||||
if (keyguardManager != null && keyguardManager.isKeyguardSecure() && appPreferences.getIsScreenLocked()) {
|
||||
if (!SecurityUtils.checkIfWeAreAuthenticated()) {
|
||||
showAuthenticationScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showAuthenticationScreen() {
|
||||
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null);
|
||||
if (intent != null) {
|
||||
startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (SecurityUtils.checkIfWeAreAuthenticated()) {
|
||||
// all went well
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we didnt auth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,8 @@ package com.nextcloud.talk.controllers;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@ -36,7 +38,13 @@ import android.view.WindowManager;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||
@ -61,37 +69,18 @@ import com.nextcloud.talk.utils.glide.GlideApp;
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences;
|
||||
import com.nextcloud.talk.utils.preferences.MagicUserInputModule;
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder;
|
||||
import com.yarolegovich.mp.MaterialChoicePreference;
|
||||
import com.yarolegovich.mp.MaterialEditTextPreference;
|
||||
import com.yarolegovich.mp.MaterialPreferenceCategory;
|
||||
import com.yarolegovich.mp.MaterialPreferenceScreen;
|
||||
import com.yarolegovich.mp.MaterialStandardPreference;
|
||||
import com.yarolegovich.mp.MaterialSwitchPreference;
|
||||
|
||||
import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import com.yarolegovich.mp.*;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class SettingsController extends BaseController {
|
||||
@ -164,6 +153,9 @@ public class SettingsController extends BaseController {
|
||||
@BindView(R.id.settings_link_previews)
|
||||
MaterialSwitchPreference linkPreviewsSwitchPreference;
|
||||
|
||||
@BindView(R.id.settings_screen_lock)
|
||||
MaterialSwitchPreference screenLockSwitchPreference;
|
||||
|
||||
@BindView(R.id.message_text)
|
||||
TextView messageText;
|
||||
|
||||
@ -179,6 +171,9 @@ public class SettingsController extends BaseController {
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
|
||||
@Inject
|
||||
Context context;
|
||||
|
||||
private UserEntity currentUser;
|
||||
private String credentials;
|
||||
|
||||
@ -235,9 +230,18 @@ public class SettingsController extends BaseController {
|
||||
shouldVibrateSwitchPreference.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
incognitoKeyboardSwitchPreference.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
screenLockSwitchPreference.setVisibility(View.GONE);
|
||||
} else {
|
||||
screenLockSwitchPreference.setSummary(String.format(Locale.getDefault(),
|
||||
getResources().getString(R.string.nc_settings_screen_lock_desc),
|
||||
getResources().getString(R.string.nc_app_name)));
|
||||
}
|
||||
|
||||
|
||||
if (!TextUtils.isEmpty(getResources().getString(R.string.nc_privacy_url))) {
|
||||
privacyButton.addPreferenceClickListener(view12 -> {
|
||||
@ -343,15 +347,34 @@ public class SettingsController extends BaseController {
|
||||
}
|
||||
|
||||
if (shouldVibrateSwitchPreference.getVisibility() == View.VISIBLE) {
|
||||
((Checkable)shouldVibrateSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getShouldVibrateSetting());
|
||||
((Checkable) shouldVibrateSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getShouldVibrateSetting());
|
||||
}
|
||||
|
||||
((Checkable)screenSecuritySwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenSecured());
|
||||
((Checkable) screenSecuritySwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenSecured());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito());
|
||||
}
|
||||
((Checkable)linkPreviewsSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getAreLinkPreviewsAllowed());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
((Checkable) incognitoKeyboardSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsKeyboardIncognito());
|
||||
}
|
||||
|
||||
((Checkable) linkPreviewsSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getAreLinkPreviewsAllowed());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
|
||||
if (keyguardManager.isDeviceSecure()) {
|
||||
screenLockSwitchPreference.setEnabled(true);
|
||||
((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getIsScreenLocked());
|
||||
screenLockSwitchPreference.setAlpha(1.0f);
|
||||
} else {
|
||||
screenLockSwitchPreference.setEnabled(false);
|
||||
appPreferences.setScreenLock(false);
|
||||
((Checkable) screenLockSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(false);
|
||||
screenLockSwitchPreference.setAlpha(0.38f);
|
||||
}
|
||||
}
|
||||
|
||||
String ringtoneName = "";
|
||||
RingtoneSettings ringtoneSettings;
|
||||
@ -633,6 +656,7 @@ public class SettingsController extends BaseController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyCredentialsChangeListener implements OnPreferenceValueChangedListener<Boolean> {
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils;
|
||||
|
||||
import android.os.Build;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
import android.security.keystore.KeyProperties;
|
||||
import android.security.keystore.UserNotAuthenticatedException;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import javax.crypto.*;
|
||||
import java.io.IOException;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
public class SecurityUtils {
|
||||
private static final String TAG = "SecurityUtils";
|
||||
private static final String CREDENTIALS_KEY = "KEY_CREDENTIALS";
|
||||
private static final byte[] SECRET_BYTE_ARRAY = new byte[]{1, 2, 3, 4, 5, 6};
|
||||
|
||||
private static final int AUTHENTICATION_DURATION_SECONDS = 10;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static boolean checkIfWeAreAuthenticated() {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||
keyStore.load(null);
|
||||
SecretKey secretKey = (SecretKey) keyStore.getKey(CREDENTIALS_KEY, 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, we will reach here
|
||||
return true;
|
||||
} catch (UserNotAuthenticatedException e) {
|
||||
// User is not authenticated, let's authenticate with device credentials.
|
||||
return false;
|
||||
} catch (KeyPermanentlyInvalidatedException e) {
|
||||
return false;
|
||||
} catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException |
|
||||
CertificateException | UnrecoverableKeyException | IOException
|
||||
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static void createKey() {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||
keyStore.load(null);
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(
|
||||
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
|
||||
|
||||
keyGenerator.init(new KeyGenParameterSpec.Builder(CREDENTIALS_KEY,
|
||||
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.e(TAG, "Failed to create a symetric key");
|
||||
}
|
||||
}
|
||||
}
|
@ -149,6 +149,14 @@
|
||||
apc:mpc_title="@string/nc_settings_privacy"
|
||||
apc:mpc_title_color="@color/colorPrimary">
|
||||
|
||||
<com.yarolegovich.mp.MaterialSwitchPreference
|
||||
android:id="@+id/settings_screen_lock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
apc:mp_default_value="@bool/value_false"
|
||||
apc:mp_key="@string/nc_settings_screen_lock_key"
|
||||
apc:mp_title="@string/nc_settings_screen_lock_title" />
|
||||
|
||||
<com.yarolegovich.mp.MaterialSwitchPreference
|
||||
android:id="@+id/settings_screen_security"
|
||||
android:layout_width="match_parent"
|
||||
|
Loading…
Reference in New Issue
Block a user