Merge pull request #2586 from open-keychain/drop-more-stuff
Drop more stuff
This commit is contained in:
commit
258cd4c836
96 changed files with 140 additions and 3872 deletions
|
@ -49,9 +49,6 @@ dependencies {
|
|||
// Nordpol
|
||||
implementation 'com.fidesmo:nordpol-android:0.1.22'
|
||||
|
||||
// piwik
|
||||
implementation 'org.piwik.sdk:piwik-sdk:3.0.3'
|
||||
|
||||
// libs as submodules
|
||||
implementation project(':openpgp-api-lib')
|
||||
implementation project(':nfcsweetspot')
|
||||
|
|
|
@ -54,21 +54,6 @@
|
|||
|
||||
<!-- CAMERA permission requested by ZXing library -->
|
||||
|
||||
<!-- contact group -->
|
||||
<!--
|
||||
AUTHENTICATE_ACCOUNTS and MANAGE_ACCOUNTS removed in Android >= 6,
|
||||
see https://code.google.com/p/android-developer-preview/issues/detail?id=2592
|
||||
also READ_PROFILE, WRITE_PROFILE?
|
||||
-->
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.READ_PROFILE" />
|
||||
<uses-permission android:name="android.permission.WRITE_PROFILE" />
|
||||
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
|
||||
<!-- storage group -->
|
||||
<!--
|
||||
No need on >= Android 4.4 for WRITE_EXTERNAL_STORAGE, because we use Storage Access Framework,
|
||||
|
@ -1012,36 +997,6 @@
|
|||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- Contact Sync services -->
|
||||
<service
|
||||
android:name=".service.DummyAccountService"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/account_desc" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".service.ContactSyncAdapterService"
|
||||
android:exported="true"
|
||||
android:process=":sync"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_adapter_contacts" />
|
||||
<meta-data
|
||||
android:name="android.provider.CONTACTS_STRUCTURE"
|
||||
android:resource="@xml/sync_adapter_contacts_structure" />
|
||||
</service>
|
||||
|
||||
<!-- Storage Provider for temporary decrypted files.
|
||||
For security considerations, read class! -->
|
||||
<provider
|
||||
|
|
|
@ -20,8 +20,6 @@ package org.sufficientlysecure.keychain;
|
|||
|
||||
import java.io.File;
|
||||
import java.net.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
|
@ -159,10 +157,6 @@ public final class Constants {
|
|||
|
||||
public static final String KEY_SIGNATURES_TABLE_INITIALIZED = "key_signatures_table_initialized";
|
||||
|
||||
public static final String KEY_ANALYTICS_ASKED_POLITELY = "analytics_asked";
|
||||
public static final String KEY_ANALYTICS_CONSENT = "analytics_consent";
|
||||
public static final String KEY_ANALYTICS_LAST_ASKED = "analytics_last_asked";
|
||||
|
||||
public static final class Theme {
|
||||
public static final String LIGHT = "light";
|
||||
public static final String DARK = "dark";
|
||||
|
@ -173,14 +167,6 @@ public final class Constants {
|
|||
public static final String TYPE_HTTP = "proxyHttp";
|
||||
public static final String TYPE_SOCKS = "proxySocks";
|
||||
}
|
||||
|
||||
// we generally only track booleans. never snoop around in the user's string settings!!
|
||||
public static final List<String> ANALYTICS_PREFS = Arrays.asList(USE_NORMAL_PROXY, USE_TOR_PROXY,
|
||||
SYNC_CONTACTS, SYNC_KEYSERVER, ENABLE_WIFI_SYNC_ONLY,
|
||||
EXPERIMENTAL_USB_ALLOW_UNTESTED,
|
||||
PASSPHRASE_CACHE_SUBS, SEARCH_KEYSERVER, SEARCH_WEB_KEY_DIRECTORY,
|
||||
TEXT_USE_COMPRESSION, TEXT_SELF_ENCRYPT, FILE_USE_COMPRESSION, FILE_SELF_ENCRYPT, USE_ARMOR,
|
||||
USE_NUMKEYPAD_FOR_SECURITY_TOKEN_PIN, ENCRYPT_FILENAMES);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,15 +30,15 @@ import android.app.Application;
|
|||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsManager;
|
||||
import org.sufficientlysecure.keychain.keysync.KeyserverSyncManager;
|
||||
import org.sufficientlysecure.keychain.network.TlsCertificatePinning;
|
||||
import org.sufficientlysecure.keychain.provider.TemporaryFileProvider;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.util.PRNGFixes;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import timber.log.Timber;
|
||||
|
@ -46,8 +46,6 @@ import timber.log.Timber.DebugTree;
|
|||
|
||||
|
||||
public class KeychainApplication extends Application {
|
||||
AnalyticsManager analyticsManager;
|
||||
|
||||
/**
|
||||
* Called when the application is starting, before any activity, service, or receiver objects
|
||||
* (excluding content providers) have been created.
|
||||
|
@ -88,15 +86,9 @@ public class KeychainApplication extends Application {
|
|||
}
|
||||
*/
|
||||
|
||||
// Add OpenKeychain account to Android to link contacts with keys and keyserver sync
|
||||
createAccountIfNecessary(this);
|
||||
|
||||
Preferences preferences = Preferences.getPreferences(this);
|
||||
if (preferences.isAppExecutedFirstTime()) {
|
||||
preferences.setAppExecutedFirstTime(false);
|
||||
|
||||
ContactSyncAdapterService.enableContactsSync(this);
|
||||
|
||||
preferences.setPrefVersionToCurrentVersion();
|
||||
}
|
||||
|
||||
|
@ -116,36 +108,6 @@ public class KeychainApplication extends Application {
|
|||
KeyserverSyncManager.updateKeyserverSyncScheduleAsync(this, false);
|
||||
|
||||
TemporaryFileProvider.scheduleCleanupImmediately(getApplicationContext());
|
||||
|
||||
analyticsManager = AnalyticsManager.getInstance(getApplicationContext());
|
||||
analyticsManager.initialize(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the OpenKeychain contact/keyserver sync account if it exists or was successfully
|
||||
* created, null otherwise
|
||||
*/
|
||||
public static @Nullable Account createAccountIfNecessary(Context context) {
|
||||
try {
|
||||
AccountManager manager = AccountManager.get(context);
|
||||
Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
|
||||
|
||||
Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
|
||||
if (accounts.length == 0) {
|
||||
if (!manager.addAccountExplicitly(account, null, null)) {
|
||||
Timber.d("error when adding account via addAccountExplicitly");
|
||||
return null;
|
||||
} else {
|
||||
return account;
|
||||
}
|
||||
} else {
|
||||
return accounts[0];
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Timber.e(e, "SecurityException when adding the account");
|
||||
Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static HashMap<String,Bitmap> qrCodeCache = new HashMap<>();
|
||||
|
@ -167,10 +129,6 @@ public class KeychainApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
public AnalyticsManager getAnalyticsManager() {
|
||||
return analyticsManager;
|
||||
}
|
||||
|
||||
public static String getProcessName() {
|
||||
if (Build.VERSION.SDK_INT >= 28)
|
||||
return Application.getProcessName();
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
package org.sufficientlysecure.keychain.analytics;
|
||||
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.BuildConfig;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.SettingsActivity;
|
||||
import org.sufficientlysecure.keychain.ui.SettingsActivity.ExperimentalPrefsFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
|
||||
public class AnalyticsConsentRequester {
|
||||
private final Activity activity;
|
||||
|
||||
public static AnalyticsConsentRequester getInstance(Activity activity) {
|
||||
return new AnalyticsConsentRequester(activity);
|
||||
}
|
||||
|
||||
private AnalyticsConsentRequester(Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
public void maybeAskForAnalytics() {
|
||||
Preferences preferences = Preferences.getPreferences(activity);
|
||||
if (preferences.isAnalyticsHasConsent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean askedBeforeAndWasRejected =
|
||||
preferences.isAnalyticsAskedPolitely() && !preferences.isAnalyticsHasConsent();
|
||||
if (askedBeforeAndWasRejected) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
long firstInstallTime =
|
||||
activity.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, 0).firstInstallTime;
|
||||
long threeDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(3);
|
||||
boolean installedLessThanThreeDaysAgo = firstInstallTime > threeDaysAgo;
|
||||
if (installedLessThanThreeDaysAgo) {
|
||||
return;
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
long twentyFourHoursAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
|
||||
boolean askedLessThan24HoursAgo = preferences.getAnalyticsLastAsked() > twentyFourHoursAgo;
|
||||
if (askedLessThan24HoursAgo) {
|
||||
return;
|
||||
}
|
||||
|
||||
preferences.setAnalyticsLastAskedNow();
|
||||
|
||||
AnalyticsManager analyticsManager = ((KeychainApplication) activity.getApplication()).getAnalyticsManager();
|
||||
AlertDialog alertDialog = new Builder(activity)
|
||||
.setMessage(R.string.dialog_analytics_consent)
|
||||
.setPositiveButton(R.string.button_analytics_yes, (dialog, which) -> {
|
||||
preferences.setAnalyticsAskedPolitely();
|
||||
preferences.setAnalyticsGotUserConsent(true);
|
||||
analyticsManager.refreshSettings(activity);
|
||||
Notify.create(activity, R.string.snack_analytics_accept, Style.OK,
|
||||
this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show();
|
||||
})
|
||||
.setNegativeButton(R.string.button_analytics_no, (dialog, which) -> {
|
||||
preferences.setAnalyticsAskedPolitely();
|
||||
preferences.setAnalyticsGotUserConsent(false);
|
||||
analyticsManager.refreshSettings(activity);
|
||||
Notify.create(activity, R.string.snack_analytics_reject, Style.OK,
|
||||
this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show();
|
||||
})
|
||||
.show();
|
||||
alertDialog.<TextView>findViewById(android.R.id.message).setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private void startExperimentalSettingsActivity() {
|
||||
Intent resultIntent = new Intent(activity, SettingsActivity.class);
|
||||
String experimentalPrefsName = ExperimentalPrefsFragment.class.getName();
|
||||
resultIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, experimentalPrefsName);
|
||||
activity.startActivity(resultIntent);
|
||||
}
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
package org.sufficientlysecure.keychain.analytics;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Application.ActivityLifecycleCallbacks;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.piwik.sdk.Piwik;
|
||||
import org.piwik.sdk.Tracker;
|
||||
import org.piwik.sdk.TrackerConfig;
|
||||
import org.piwik.sdk.extra.DownloadTracker.Extra.ApkChecksum;
|
||||
import org.piwik.sdk.extra.TrackHelper;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Constants.Defaults;
|
||||
import org.sufficientlysecure.keychain.Constants.Pref;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class AnalyticsManager implements OnSharedPreferenceChangeListener {
|
||||
private Tracker piwikTracker;
|
||||
|
||||
public static AnalyticsManager getInstance(Context context) {
|
||||
return new AnalyticsManager(context);
|
||||
}
|
||||
|
||||
private AnalyticsManager(Context context) {
|
||||
refreshSettings(context);
|
||||
}
|
||||
|
||||
public void initialize(Application application) {
|
||||
if (piwikTracker != null) {
|
||||
TrackHelper.track().download().identifier(new ApkChecksum(application)).with(piwikTracker);
|
||||
}
|
||||
|
||||
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
if (piwikTracker == null) {
|
||||
return;
|
||||
}
|
||||
TrackHelper.track().screen(activity.getClass().getSimpleName()).with(piwikTracker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
Preferences preferences = Preferences.getPreferences(application);
|
||||
preferences.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
// we generally only track booleans. never snoop around in the user's string settings!!
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (piwikTracker == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// small exception: check if the user uses a custom keyserver, or one of the well-known ones
|
||||
if (Pref.KEY_SERVERS.equals(key)) {
|
||||
Timber.d("Tracking pref %s", key);
|
||||
String keyServers = sharedPreferences.getString(Pref.KEY_SERVERS, Defaults.KEY_SERVERS);
|
||||
String current = keyServers.substring(keyServers.indexOf(','));
|
||||
|
||||
String coarseGranularityKeyserver;
|
||||
if (current.contains("keyserver.ubuntu.com")) {
|
||||
coarseGranularityKeyserver = "ubuntu";
|
||||
} else if (current.contains("pgp.mit.edu")) {
|
||||
coarseGranularityKeyserver = "mit";
|
||||
} else if (current.contains("pool.sks-keyservers.net")) {
|
||||
coarseGranularityKeyserver = "pool";
|
||||
} else {
|
||||
coarseGranularityKeyserver = "custom";
|
||||
}
|
||||
TrackHelper.track().interaction("pref_" + Pref.KEY_SERVERS, coarseGranularityKeyserver).with(piwikTracker);
|
||||
return;
|
||||
}
|
||||
// unpack an enum
|
||||
if (Pref.THEME.equals(key)) {
|
||||
String value = sharedPreferences.getString(Pref.THEME, "empty");
|
||||
TrackHelper.track().interaction("pref_" + Pref.THEME, value).with(piwikTracker);
|
||||
return;
|
||||
}
|
||||
// all other values we track are individual booleans
|
||||
if (Pref.ANALYTICS_PREFS.contains(key)) {
|
||||
Timber.d("Tracking pref %s", key);
|
||||
if (!sharedPreferences.contains(key)) {
|
||||
TrackHelper.track().interaction("pref_" + key, "empty").with(piwikTracker);
|
||||
return;
|
||||
}
|
||||
boolean value = sharedPreferences.getBoolean(key, false);
|
||||
TrackHelper.track().interaction("pref_" + key, value ? "true" : "false").with(piwikTracker);
|
||||
}
|
||||
}
|
||||
|
||||
public void trackFragmentImpression(String opClassName, String fragmentName) {
|
||||
if (piwikTracker == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackHelper.track().screen(opClassName + "/" + fragmentName).with(piwikTracker);
|
||||
}
|
||||
|
||||
public void trackInternalServiceCall(String opClassName) {
|
||||
if (piwikTracker == null) {
|
||||
return;
|
||||
}
|
||||
TrackHelper.track()
|
||||
.interaction("internalApiCall", opClassName)
|
||||
.with(piwikTracker);
|
||||
}
|
||||
|
||||
public void trackApiServiceCall(String opClassName, String currentCallingPackage) {
|
||||
if (piwikTracker == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackHelper.track()
|
||||
.interaction("externalApiCall", opClassName)
|
||||
.piece(currentCallingPackage.replace(".", "/"))
|
||||
.with(piwikTracker);
|
||||
}
|
||||
|
||||
public synchronized void refreshSettings(Context context) {
|
||||
boolean shouldEnableAnalytics = shouldEnableAnalytics(context);
|
||||
boolean analyticsEnabled = piwikTracker != null;
|
||||
if (shouldEnableAnalytics != analyticsEnabled) {
|
||||
if (shouldEnableAnalytics) {
|
||||
TrackerConfig trackerConfig;
|
||||
if (Constants.DEBUG) {
|
||||
trackerConfig = new TrackerConfig("https://piwik.openkeychain.org/", 3, "OpenKeychainDebug");
|
||||
} else {
|
||||
trackerConfig = new TrackerConfig("https://piwik.openkeychain.org/", 2, "OpenKeychain");
|
||||
}
|
||||
piwikTracker = Piwik.getInstance(context).newTracker(trackerConfig);
|
||||
piwikTracker.setDispatchInterval(60000);
|
||||
piwikTracker.setOptOut(false);
|
||||
} else {
|
||||
piwikTracker.setOptOut(true);
|
||||
piwikTracker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldEnableAnalytics(Context context) {
|
||||
Preferences preferences = Preferences.getPreferences(context);
|
||||
return preferences.isAnalyticsHasConsent() && !preferences.getUseTorProxy();
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@
|
|||
package org.sufficientlysecure.keychain.keysync;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -29,11 +28,10 @@ import android.os.Build.VERSION_CODES;
|
|||
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.work.Constraints.Builder;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.NetworkType;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkInfo;
|
||||
import androidx.work.WorkInfo.State;
|
||||
import androidx.work.WorkManager;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import timber.log.Timber;
|
||||
|
@ -43,7 +41,8 @@ public class KeyserverSyncManager {
|
|||
private static final long SYNC_INTERVAL = 3;
|
||||
private static final TimeUnit SYNC_INTERVAL_UNIT = TimeUnit.DAYS;
|
||||
|
||||
private static final String PERIODIC_WORK_TAG = "keyserverSync";
|
||||
private static final String LEGACY_PERIODIC_WORK_TAG = "keyserverSync";
|
||||
private static final String WORK_UNIQUE_NAME = "periodicKeyserverSync";
|
||||
|
||||
public static void updateKeyserverSyncScheduleAsync(Context context, boolean forceReschedule) {
|
||||
new AsyncTask<Void,Void,Void>() {
|
||||
|
@ -60,27 +59,12 @@ public class KeyserverSyncManager {
|
|||
Preferences prefs = Preferences.getPreferences(context);
|
||||
WorkManager workManager = WorkManager.getInstance(context);
|
||||
|
||||
UUID workUuid = prefs.getKeyserverSyncWorkUuid();
|
||||
try {
|
||||
WorkInfo info = workUuid != null ? workManager.getWorkInfoById(workUuid).get() : null;
|
||||
|
||||
boolean workIsScheduled = info != null && info.getState() != State.CANCELLED;
|
||||
if (workIsScheduled == prefs.isKeyserverSyncEnabled()) {
|
||||
if (!forceReschedule) {
|
||||
Timber.d("Key sync already scheduled, no changes necessary");
|
||||
return;
|
||||
}
|
||||
Timber.d("Key sync already scheduled, but forcing reschedule");
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
Timber.e(e, "Error getting info for scheduled key sync work?");
|
||||
}
|
||||
|
||||
Timber.d("Cancelling sync tasks…");
|
||||
workManager.cancelAllWorkByTag(PERIODIC_WORK_TAG);
|
||||
// Cancel work that was scheduled by tag, as we used to do.
|
||||
workManager.cancelAllWorkByTag(LEGACY_PERIODIC_WORK_TAG);
|
||||
|
||||
if (!prefs.isKeyserverSyncEnabled()) {
|
||||
Timber.d("Key sync disabled");
|
||||
workManager.cancelUniqueWork(WORK_UNIQUE_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -96,10 +80,12 @@ public class KeyserverSyncManager {
|
|||
PeriodicWorkRequest workRequest =
|
||||
new PeriodicWorkRequest.Builder(KeyserverSyncWorker.class, SYNC_INTERVAL, SYNC_INTERVAL_UNIT)
|
||||
.setConstraints(constraints.build())
|
||||
.addTag(PERIODIC_WORK_TAG)
|
||||
.build();
|
||||
try {
|
||||
workManager.enqueue(workRequest).getResult().get();
|
||||
ExistingPeriodicWorkPolicy policy = forceReschedule
|
||||
? ExistingPeriodicWorkPolicy.REPLACE
|
||||
: ExistingPeriodicWorkPolicy.KEEP;
|
||||
workManager.enqueueUniquePeriodicWork(WORK_UNIQUE_NAME, policy, workRequest).getResult().get();
|
||||
Timber.d("Work id: %s", workRequest.getId());
|
||||
prefs.setKeyserverSyncScheduled(workRequest.getId());
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
|||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
@ -254,9 +253,6 @@ public class CertifyOperation extends BaseReadWriteOperation<CertifyActionsParce
|
|||
uploadOk, uploadError);
|
||||
}
|
||||
|
||||
// since only verified keys are synced to contacts, we need to initiate a sync now
|
||||
ContactSyncAdapterService.requestContactsSync();
|
||||
|
||||
log.add(LogType.MSG_CRT_SUCCESS, 0);
|
||||
if (uploadError != 0) {
|
||||
return new CertifyResult(CertifyResult.RESULT_WARNINGS, log, certifyOk, certifyError, uploadOk,
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
|
|||
import org.sufficientlysecure.keychain.operations.results.UpdateTrustResult;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
@ -101,9 +100,6 @@ public class DeleteOperation extends BaseReadWriteOperation<DeleteKeyringParcel>
|
|||
|
||||
int result = DeleteResult.RESULT_OK;
|
||||
if (success > 0) {
|
||||
// make sure new data is synced into contacts
|
||||
ContactSyncAdapterService.requestContactsSync();
|
||||
|
||||
log.add(LogType.MSG_DEL_OK, 0, success);
|
||||
}
|
||||
if (fail > 0) {
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
|
|||
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
|
@ -182,9 +181,6 @@ public class EditKeyOperation extends BaseReadWriteOperation<SaveKeyringParcel>
|
|||
|
||||
updateProgress(R.string.progress_done, 100, 100);
|
||||
|
||||
// make sure new data is synced into contacts
|
||||
ContactSyncAdapterService.requestContactsSync();
|
||||
|
||||
log.add(LogType.MSG_ED_SUCCESS, 0);
|
||||
return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.daos.KeyMetadataDao;
|
||||
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
|
||||
|
@ -55,7 +55,6 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
|
|||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
@ -134,9 +133,6 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
|
|||
}
|
||||
|
||||
/**
|
||||
* Since the introduction of multithreaded import, we expect calling functions to handle the
|
||||
* contact-to-key sync i.e ContactSyncAdapterService.requestContactsSync()
|
||||
*
|
||||
* @param entries keys to import
|
||||
* @param numTotalKeys number of keys to import
|
||||
* @param hkpKeyserver contains uri of keyserver to import from, if it is an import from cloud
|
||||
|
@ -274,11 +270,6 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
|
|||
}
|
||||
}
|
||||
|
||||
// Special: make sure new data is synced into contacts
|
||||
// disabling sync right now since it reduces speed while multi-threading
|
||||
// so, we expect calling functions to take care of it. KeychainService handles this
|
||||
// ContactSyncAdapterService.requestContactsSync();
|
||||
|
||||
// convert to long array
|
||||
long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
|
||||
for (int i = 0; i < importedMasterKeyIds.size(); ++i) {
|
||||
|
@ -476,10 +467,6 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
|
|||
|
||||
result = multiThreadedKeyImport(keyList, keyServer, proxy, skipSave, forceReinsert);
|
||||
}
|
||||
|
||||
if (!skipSave) {
|
||||
ContactSyncAdapterService.requestContactsSync();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
|
@ -41,6 +40,7 @@ import android.os.ParcelFileDescriptor;
|
|||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import androidx.work.Worker;
|
||||
|
@ -77,6 +77,7 @@ public class TemporaryFileProvider extends ContentProvider {
|
|||
public static final String AUTHORITY = Constants.TEMP_FILE_PROVIDER_AUTHORITY;
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
|
||||
private static final int DB_VERSION = 3;
|
||||
public static final String WORK_NAME_CLEANUP = "cleanup";
|
||||
|
||||
interface TemporaryFileColumns {
|
||||
String COLUMN_UUID = "id";
|
||||
|
@ -315,20 +316,22 @@ public class TemporaryFileProvider extends ContentProvider {
|
|||
public static void scheduleCleanupAfterTtl(Context context) {
|
||||
OneTimeWorkRequest cleanupWork = new OneTimeWorkRequest.Builder(CleanupWorker.class)
|
||||
.setInitialDelay(Constants.TEMPFILE_TTL, TimeUnit.MILLISECONDS).build();
|
||||
workManagerEnqueue(context, cleanupWork);
|
||||
workManagerEnqueueCleanup(context, cleanupWork);
|
||||
}
|
||||
|
||||
public static void scheduleCleanupImmediately(Context context) {
|
||||
OneTimeWorkRequest cleanupWork = new OneTimeWorkRequest.Builder(CleanupWorker.class).build();
|
||||
workManagerEnqueue(context, cleanupWork);
|
||||
workManagerEnqueueCleanup(context, cleanupWork);
|
||||
}
|
||||
|
||||
private static void workManagerEnqueue(Context context, OneTimeWorkRequest cleanupWork) {
|
||||
private static void workManagerEnqueueCleanup(Context context, OneTimeWorkRequest cleanupWork) {
|
||||
// work manager is only available on the main thread
|
||||
if (!BuildConfig.APPLICATION_ID.equals(KeychainApplication.getProcessName())) {
|
||||
return;
|
||||
}
|
||||
WorkManager.getInstance(context).enqueue(cleanupWork);
|
||||
WorkManager
|
||||
.getInstance(context)
|
||||
.enqueueUniqueWork(TemporaryFileProvider.WORK_NAME_CLEANUP, ExistingWorkPolicy.REPLACE, cleanupWork);
|
||||
}
|
||||
|
||||
public static class CleanupWorker extends Worker {
|
||||
|
|
|
@ -38,10 +38,10 @@ import android.os.Messenger;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||
import org.openintents.openpgp.AutocryptPeerUpdate;
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
|
@ -52,9 +52,12 @@ import org.openintents.openpgp.OpenPgpSignatureResult;
|
|||
import org.openintents.openpgp.OpenPgpSignatureResult.AutocryptPeerResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.daos.ApiAppDao;
|
||||
import org.sufficientlysecure.keychain.daos.AutocryptPeerDao;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.daos.OverriddenWarningsDao;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsManager;
|
||||
import org.sufficientlysecure.keychain.operations.BackupOperation;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
|
@ -69,12 +72,7 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData;
|
|||
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem;
|
||||
import org.sufficientlysecure.keychain.daos.ApiAppDao;
|
||||
import org.sufficientlysecure.keychain.daos.AutocryptPeerDao;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus;
|
||||
import org.sufficientlysecure.keychain.daos.OverriddenWarningsDao;
|
||||
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResult;
|
||||
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResultStatus;
|
||||
import org.sufficientlysecure.keychain.service.BackupKeyringParcel;
|
||||
|
@ -101,7 +99,6 @@ public class OpenPgpService extends Service {
|
|||
private ApiAppDao mApiAppDao;
|
||||
private OpenPgpServiceKeyIdExtractor mKeyIdExtractor;
|
||||
private ApiPendingIntentFactory mApiPendingIntentFactory;
|
||||
private AnalyticsManager analyticsManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
@ -111,8 +108,6 @@ public class OpenPgpService extends Service {
|
|||
mApiPermissionHelper = new ApiPermissionHelper(this, mApiAppDao);
|
||||
mApiPendingIntentFactory = new ApiPendingIntentFactory(getBaseContext());
|
||||
mKeyIdExtractor = OpenPgpServiceKeyIdExtractor.getInstance(getContentResolver(), mApiPendingIntentFactory);
|
||||
|
||||
analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager();
|
||||
}
|
||||
|
||||
private Intent signImpl(Intent data, InputStream inputStream,
|
||||
|
@ -1032,8 +1027,6 @@ public class OpenPgpService extends Service {
|
|||
return errorResult;
|
||||
}
|
||||
|
||||
analyticsManager.trackApiServiceCall(data.getAction(), mApiPermissionHelper.getCurrentCallingPackage());
|
||||
|
||||
Progressable progressable = null;
|
||||
if (data.hasExtra(OpenPgpApi.EXTRA_PROGRESS_MESSENGER)) {
|
||||
Messenger messenger = data.getParcelableExtra(OpenPgpApi.EXTRA_PROGRESS_MESSENGER);
|
||||
|
|
|
@ -40,17 +40,15 @@ import org.openintents.ssh.authentication.response.PublicKeyResponse;
|
|||
import org.openintents.ssh.authentication.response.SigningResponse;
|
||||
import org.openintents.ssh.authentication.response.SshPublicKeyResponse;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsManager;
|
||||
import org.sufficientlysecure.keychain.daos.ApiAppDao;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
|
||||
import org.sufficientlysecure.keychain.pgp.SshPublicKey;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.daos.ApiAppDao;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ssh.AuthenticationData;
|
||||
|
@ -67,8 +65,6 @@ public class SshAuthenticationService extends Service {
|
|||
private ApiAppDao mApiAppDao;
|
||||
private ApiPendingIntentFactory mApiPendingIntentFactory;
|
||||
|
||||
private AnalyticsManager analyticsManager;
|
||||
|
||||
private static final List<Integer> SUPPORTED_VERSIONS = Collections.unmodifiableList(Collections.singletonList(1));
|
||||
private static final int INVALID_API_VERSION = -1;
|
||||
|
||||
|
@ -82,8 +78,6 @@ public class SshAuthenticationService extends Service {
|
|||
mApiAppDao = ApiAppDao.getInstance(this);
|
||||
|
||||
mApiPendingIntentFactory = new ApiPendingIntentFactory(getBaseContext());
|
||||
|
||||
analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager();
|
||||
}
|
||||
|
||||
private final ISshAuthenticationService.Stub mSSHAgent = new ISshAuthenticationService.Stub() {
|
||||
|
@ -109,8 +103,6 @@ public class SshAuthenticationService extends Service {
|
|||
}
|
||||
|
||||
private Intent executeInternal(Intent intent) {
|
||||
analyticsManager.trackApiServiceCall(intent.getAction(), mApiPermissionHelper.getCurrentCallingPackage());
|
||||
|
||||
switch (intent.getAction()) {
|
||||
case SshAuthenticationApi.ACTION_SIGN:
|
||||
return authenticate(intent);
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.service;
|
||||
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SyncResult;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.provider.ContactsContract;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.NotificationChannelManager;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.SettingsActivity;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class ContactSyncAdapterService extends Service {
|
||||
|
||||
private static final int NOTIFICATION_ID_SYNC_SETTINGS = 13;
|
||||
|
||||
private class ContactSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
|
||||
// private final AtomicBoolean importDone = new AtomicBoolean(false);
|
||||
|
||||
public ContactSyncAdapter() {
|
||||
super(ContactSyncAdapterService.this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
|
||||
final SyncResult syncResult) {
|
||||
Timber.d("Performing a contact sync!");
|
||||
|
||||
new ContactHelper(ContactSyncAdapterService.this).writeKeysToContacts();
|
||||
|
||||
// importKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSecurityException(Account account, Bundle extras, String authority, SyncResult syncResult) {
|
||||
super.onSecurityException(account, extras, authority, syncResult);
|
||||
|
||||
// deactivate sync
|
||||
ContentResolver.setSyncAutomatically(account, authority, false);
|
||||
|
||||
NotificationChannelManager.getInstance(getContext()).createNotificationChannelsIfNecessary();
|
||||
|
||||
// show notification linking to sync settings
|
||||
Intent resultIntent = new Intent(ContactSyncAdapterService.this, SettingsActivity.class);
|
||||
resultIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
|
||||
SettingsActivity.SyncPrefsFragment.class.getName());
|
||||
PendingIntent resultPendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
ContactSyncAdapterService.this,
|
||||
0,
|
||||
resultIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
NotificationCompat.Builder mBuilder =
|
||||
new NotificationCompat.Builder(ContactSyncAdapterService.this, NotificationChannelManager.PERMISSION_REQUESTS)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify_24dp)
|
||||
.setColor(getResources().getColor(R.color.primary))
|
||||
.setContentTitle(getString(R.string.sync_notification_permission_required_title))
|
||||
.setContentText(getString(R.string.sync_notification_permission_required_text))
|
||||
.setContentIntent(resultPendingIntent);
|
||||
NotificationManagerCompat.from(ContactSyncAdapterService.this)
|
||||
.notify(NOTIFICATION_ID_SYNC_SETTINGS, mBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new ContactSyncAdapter().getSyncAdapterBinder();
|
||||
}
|
||||
|
||||
public static void requestContactsSync() {
|
||||
// if user has disabled automatic sync, do nothing
|
||||
boolean isSyncEnabled = ContentResolver.getSyncAutomatically(new Account
|
||||
(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), ContactsContract.AUTHORITY);
|
||||
|
||||
if (!isSyncEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
// no need to wait, do it immediately
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
|
||||
ContentResolver.requestSync(
|
||||
new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
|
||||
ContactsContract.AUTHORITY,
|
||||
extras);
|
||||
}
|
||||
|
||||
public static void enableContactsSync(Context context) {
|
||||
Account account = KeychainApplication.createAccountIfNecessary(context);
|
||||
if (account == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
}
|
||||
|
||||
// TODO: Import is currently disabled, until we implement proper origin management
|
||||
// private static void importKeys() {
|
||||
// importDone.set(false);
|
||||
// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
|
||||
// EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(),
|
||||
// new Handler.Callback() {
|
||||
// @Override
|
||||
// public boolean handleMessage(Message msg) {
|
||||
// Bundle data = msg.getInputData();
|
||||
// switch (msg.arg1) {
|
||||
// case KeychainIntentServiceHandler.MESSAGE_OKAY:
|
||||
// Log.d(Constants.TAG, "Syncing... Done.");
|
||||
// synchronized (importDone) {
|
||||
// importDone.set(true);
|
||||
// importDone.notifyAll();
|
||||
// }
|
||||
// return true;
|
||||
// case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS:
|
||||
// if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) &&
|
||||
// data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) {
|
||||
// Log.d(Constants.TAG, "Syncing... Progress: " +
|
||||
// data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" +
|
||||
// data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX));
|
||||
// return false;
|
||||
// }
|
||||
// default:
|
||||
// Log.d(Constants.TAG, "Syncing... " + msg.toString());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// })));
|
||||
// synchronized (importDone) {
|
||||
// try {
|
||||
// if (!importDone.get()) importDone.wait();
|
||||
// } catch (InterruptedException e) {
|
||||
// Log.w(Constants.TAG, e);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.service;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
/**
|
||||
* This service actually does nothing, it's sole task is to show a Toast if the use tries to create an account.
|
||||
*/
|
||||
public class DummyAccountService extends Service {
|
||||
|
||||
private class Toaster {
|
||||
private static final String TOAST_MESSAGE = "toast_message";
|
||||
private Context context;
|
||||
private Handler handler = new Handler(new Handler.Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
Toast.makeText(context, msg.getData().getString(TOAST_MESSAGE), Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
private Toaster(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void toast(int resourceId) {
|
||||
toast(context.getString(resourceId));
|
||||
}
|
||||
|
||||
public void toast(String message) {
|
||||
Message msg = new Message();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(TOAST_MESSAGE, message);
|
||||
msg.setData(bundle);
|
||||
handler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private class Authenticator extends AbstractAccountAuthenticator {
|
||||
|
||||
public Authenticator() {
|
||||
super(DummyAccountService.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
|
||||
Timber.d("DummyAccountService.editProperties");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
|
||||
String[] requiredFeatures, Bundle options) throws NetworkErrorException {
|
||||
response.onResult(new Bundle());
|
||||
toaster.toast(R.string.account_no_manual_account_creation);
|
||||
Timber.d("DummyAccountService.addAccount");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)
|
||||
throws NetworkErrorException {
|
||||
Timber.d("DummyAccountService.confirmCredentials");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType,
|
||||
Bundle options) throws NetworkErrorException {
|
||||
Timber.d("DummyAccountService.getAuthToken");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthTokenLabel(String authTokenType) {
|
||||
Timber.d("DummyAccountService.getAuthTokenLabel");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType,
|
||||
Bundle options) throws NetworkErrorException {
|
||||
Timber.d("DummyAccountService.updateCredentials");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)
|
||||
throws NetworkErrorException {
|
||||
Timber.d("DummyAccountService.hasFeatures");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Toaster toaster;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
toaster = new Toaster(this);
|
||||
return new Authenticator().getIBinder();
|
||||
}
|
||||
}
|
|
@ -25,10 +25,8 @@ import android.app.Activity;
|
|||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Parcelable;
|
||||
import androidx.core.os.CancellationSignal;
|
||||
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsManager;
|
||||
import androidx.core.os.CancellationSignal;
|
||||
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
|
||||
import org.sufficientlysecure.keychain.operations.BackupOperation;
|
||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||
|
@ -54,20 +52,15 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
|||
|
||||
|
||||
public class KeychainServiceTask {
|
||||
private final AnalyticsManager analyticsManager;
|
||||
|
||||
public static KeychainServiceTask create(Activity activity) {
|
||||
Context context = activity.getApplicationContext();
|
||||
KeyWritableRepository keyRepository = KeyWritableRepository.create(context);
|
||||
AnalyticsManager analyticsManager = ((KeychainApplication) activity.getApplication()).getAnalyticsManager();
|
||||
|
||||
return new KeychainServiceTask(context, keyRepository, analyticsManager);
|
||||
return new KeychainServiceTask(context, keyRepository);
|
||||
}
|
||||
|
||||
private KeychainServiceTask(Context context, KeyWritableRepository keyRepository, AnalyticsManager analyticsManager) {
|
||||
private KeychainServiceTask(Context context, KeyWritableRepository keyRepository) {
|
||||
this.context = context;
|
||||
this.keyRepository = keyRepository;
|
||||
this.analyticsManager = analyticsManager;
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
|
@ -128,8 +121,6 @@ public class KeychainServiceTask {
|
|||
return null;
|
||||
}
|
||||
|
||||
analyticsManager.trackInternalServiceCall(op.getClass().getSimpleName());
|
||||
|
||||
// noinspection unchecked, we make sure it's the correct op above
|
||||
return op.execute(inputParcel, cryptoInput);
|
||||
}
|
||||
|
|
|
@ -17,17 +17,16 @@
|
|||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -37,18 +36,20 @@ import android.widget.EditText;
|
|||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.AddEmailDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CreateKeyEmailFragment extends Fragment {
|
||||
private CreateKeyActivity mCreateKeyActivity;
|
||||
private EmailEditText mEmailEdit;
|
||||
private AppCompatEditText mEmailEdit;
|
||||
private ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels = new ArrayList<>();
|
||||
private EmailAdapter mEmailAdapter;
|
||||
|
||||
|
|
|
@ -20,20 +20,20 @@ package org.sufficientlysecure.keychain.ui;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.widget.NameEditText;
|
||||
|
||||
public class CreateKeyNameFragment extends Fragment {
|
||||
|
||||
CreateKeyActivity mCreateKeyActivity;
|
||||
NameEditText mNameEdit;
|
||||
AppCompatEditText mNameEdit;
|
||||
View mBackButton;
|
||||
View mNextButton;
|
||||
|
||||
|
|
|
@ -17,19 +17,13 @@
|
|||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.MatrixCursor;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.provider.BaseColumns;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.core.view.MenuItemCompat;
|
||||
import androidx.core.view.MenuItemCompat.OnActionExpandListener;
|
||||
import androidx.cursoradapter.widget.CursorAdapter;
|
||||
import androidx.cursoradapter.widget.SimpleCursorAdapter;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -38,18 +32,17 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.view.MenuItemCompat;
|
||||
import androidx.core.view.MenuItemCompat.OnActionExpandListener;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.processing.CloudLoaderState;
|
||||
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.Preferences.CloudSearchPrefs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static androidx.appcompat.widget.SearchView.OnQueryTextListener;
|
||||
import static androidx.appcompat.widget.SearchView.OnSuggestionListener;
|
||||
|
||||
/**
|
||||
* Consists of the search bar, search button, and search settings button
|
||||
|
@ -59,16 +52,9 @@ public class ImportKeysSearchFragment extends Fragment {
|
|||
public static final String ARG_QUERY = "query";
|
||||
public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
|
||||
|
||||
private static final String CURSOR_SUGGESTION = "suggestion";
|
||||
|
||||
private Activity mActivity;
|
||||
private ImportKeysListener mCallback;
|
||||
|
||||
private List<String> mNamesAndEmails;
|
||||
private SimpleCursorAdapter mSearchAdapter;
|
||||
|
||||
private List<String> mCurrentSuggestions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*
|
||||
|
@ -92,14 +78,6 @@ public class ImportKeysSearchFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater i, ViewGroup c, Bundle savedInstanceState) {
|
||||
ContactHelper contactHelper = new ContactHelper(mActivity);
|
||||
mNamesAndEmails = contactHelper.getContactNames();
|
||||
mNamesAndEmails.addAll(contactHelper.getContactMails());
|
||||
|
||||
mSearchAdapter = new SimpleCursorAdapter(mActivity,
|
||||
R.layout.import_keys_cloud_suggestions_item, null, new String[]{CURSOR_SUGGESTION},
|
||||
new int[]{android.R.id.text1}, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// no view, just search view
|
||||
|
@ -126,21 +104,7 @@ public class ImportKeysSearchFragment extends Fragment {
|
|||
|
||||
MenuItem searchItem = menu.findItem(R.id.menu_import_keys_cloud_search);
|
||||
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||
searchView.setSuggestionsAdapter(mSearchAdapter);
|
||||
|
||||
searchView.setOnSuggestionListener(new OnSuggestionListener() {
|
||||
@Override
|
||||
public boolean onSuggestionSelect(int position) {
|
||||
searchView.setQuery(mCurrentSuggestions.get(position), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSuggestionClick(int position) {
|
||||
searchView.setQuery(mCurrentSuggestions.get(position), true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
searchView.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
|
||||
searchView.setOnQueryTextListener(new OnQueryTextListener() {
|
||||
@Override
|
||||
|
@ -152,7 +116,6 @@ public class ImportKeysSearchFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
updateAdapter(newText);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
@ -180,20 +143,6 @@ public class ImportKeysSearchFragment extends Fragment {
|
|||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
private void updateAdapter(String query) {
|
||||
mCurrentSuggestions.clear();
|
||||
MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, CURSOR_SUGGESTION});
|
||||
for (int i = 0; i < mNamesAndEmails.size(); i++) {
|
||||
String s = mNamesAndEmails.get(i);
|
||||
if (s.toLowerCase().startsWith(query.toLowerCase())) {
|
||||
mCurrentSuggestions.add(s);
|
||||
c.addRow(new Object[]{i, s});
|
||||
}
|
||||
|
||||
}
|
||||
mSearchAdapter.changeCursor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
|
|
|
@ -54,7 +54,6 @@ import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
|
|||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.KeychainDatabase;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsConsentRequester;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
|
@ -254,8 +253,6 @@ public class KeyListFragment extends RecyclerFragment<FlexibleAdapter<FlexibleKe
|
|||
GenericViewModel viewModel = ViewModelProviders.of(this).get(GenericViewModel.class);
|
||||
LiveData<List<FlexibleKeyItem>> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems);
|
||||
liveData.observe(this, this::onLoadKeyItems);
|
||||
|
||||
AnalyticsConsentRequester.getInstance(activity).maybeAskForAnalytics();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
|
@ -17,18 +17,18 @@
|
|||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.view.View;
|
||||
|
||||
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
|
||||
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||
|
@ -37,10 +37,7 @@ import com.mikepenz.materialdrawer.DrawerBuilder;
|
|||
import com.mikepenz.materialdrawer.model.DividerDrawerItem;
|
||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
|
||||
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsManager;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.remote.ui.AppsListFragment;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
|
||||
|
@ -58,8 +55,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
public static final int ID_TRANSFER = 5;
|
||||
static final int ID_SETTINGS = 6;
|
||||
static final int ID_HELP = 7;
|
||||
static final int ID_SHOP = 8;
|
||||
static final int ID_AUTOCRYPT = 9;
|
||||
|
||||
// both of these are used for instrumentation testing only
|
||||
public static final String EXTRA_SKIP_FIRST_TIME = "skip_first_time";
|
||||
|
@ -67,7 +62,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
|
||||
public Drawer mDrawer;
|
||||
private Toolbar mToolbar;
|
||||
private AnalyticsManager analyticsManager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -78,15 +72,11 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
mToolbar.setTitle(R.string.app_name);
|
||||
setSupportActionBar(mToolbar);
|
||||
|
||||
analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager();
|
||||
|
||||
mDrawer = new DrawerBuilder()
|
||||
.withActivity(this)
|
||||
.withHeader(R.layout.main_drawer_header)
|
||||
.withToolbar(mToolbar)
|
||||
.addDrawerItems(
|
||||
new PrimaryDrawerItem().withName(R.string.nav_shop).withIcon(CommunityMaterial.Icon.cmd_shopping)
|
||||
.withIdentifier(ID_SHOP).withSelectable(false).withTypeface(Typeface.DEFAULT_BOLD),
|
||||
new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key)
|
||||
.withIdentifier(ID_KEYS).withSelectable(false),
|
||||
new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock)
|
||||
|
@ -102,10 +92,7 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
.withIdentifier(ID_TRANSFER).withSelectable(false),
|
||||
new DividerDrawerItem(),
|
||||
new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(ID_SETTINGS).withSelectable(false),
|
||||
new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withSelectable(false),
|
||||
new DividerDrawerItem(),
|
||||
new PrimaryDrawerItem().withName(R.string.nav_autocrypt).withIcon(GoogleMaterial.Icon.gmd_blur_on)
|
||||
.withTypeface(Typeface.DEFAULT_BOLD).withSelectable(false).withIdentifier(ID_AUTOCRYPT)
|
||||
new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withSelectable(false)
|
||||
)
|
||||
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
|
||||
@Override
|
||||
|
@ -135,12 +122,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
case ID_HELP:
|
||||
intent = new Intent(MainActivity.this, HelpActivity.class);
|
||||
break;
|
||||
case ID_SHOP:
|
||||
onShopSelected();
|
||||
break;
|
||||
case ID_AUTOCRYPT:
|
||||
onAutocryptSelected();
|
||||
break;
|
||||
}
|
||||
if (intent != null) {
|
||||
MainActivity.this.startActivity(intent);
|
||||
|
@ -219,8 +200,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
private void setFragment(Fragment frag) {
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
|
||||
analyticsManager.trackFragmentImpression(getClass().getSimpleName(), frag.getClass().getSimpleName());
|
||||
|
||||
FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.replace(R.id.main_fragment_container, frag);
|
||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||
|
@ -267,20 +246,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai
|
|||
}
|
||||
}
|
||||
|
||||
private void onShopSelected() {
|
||||
mToolbar.setTitle(R.string.shop_title);
|
||||
mDrawer.setSelection(ID_SHOP, false);
|
||||
Fragment frag = new SecurityKeyShopFragment();
|
||||
setFragment(frag);
|
||||
}
|
||||
|
||||
private void onAutocryptSelected() {
|
||||
String url = "https://addons.thunderbird.net/en-US/thunderbird/addon/autocrypt/";
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
// add the values which need to be saved from the drawer to the bundle
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.ui;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SecurityKeyShopFragment extends Fragment {
|
||||
|
||||
public static final String webShopURL = "https://shop.cotech.de/";
|
||||
public static final String referer = "https://openkeychain.shop.cotech.de";
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.security_key_shop_fragment, container, false);
|
||||
|
||||
WebView webView = view.findViewById(R.id.shop_webView);
|
||||
webView.setWebViewClient(new SecurityKeyShopWebViewClient(
|
||||
view.findViewById(R.id.shop_progressbar),
|
||||
view.findViewById(R.id.shop_progressbar_label)
|
||||
));
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Referer", referer);
|
||||
webView.loadUrl(webShopURL, headers);
|
||||
return view;
|
||||
}
|
||||
|
||||
class SecurityKeyShopWebViewClient extends WebViewClient {
|
||||
private ProgressBar progressBar;
|
||||
private TextView progressBarLabel;
|
||||
|
||||
SecurityKeyShopWebViewClient(ProgressBar progressBar, TextView progressBarLabel) {
|
||||
this.progressBar = progressBar;
|
||||
this.progressBarLabel = progressBarLabel;
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBarLabel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageCommitVisible(WebView view, String url) {
|
||||
super.onPageCommitVisible(view, url);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
progressBarLabel.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,16 +23,9 @@ import java.security.KeyStoreException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
|
@ -40,17 +33,13 @@ import android.preference.Preference;
|
|||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.provider.ContactsContract;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
|
||||
|
@ -402,11 +391,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
|
||||
// Load the preferences from an XML resource
|
||||
addPreferencesFromResource(R.xml.sync_preferences);
|
||||
|
||||
findPreference(Constants.Pref.SYNC_KEYSERVER).setOnPreferenceChangeListener(
|
||||
(preference, newValue) -> {
|
||||
return true;
|
||||
});
|
||||
(preference, newValue) -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -414,130 +400,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
super.onStop();
|
||||
KeyserverSyncManager.updateKeyserverSyncScheduleAsync(getActivity(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// this needs to be done in onResume since the user can change sync values from Android
|
||||
// settings and we need to reflect that change when the user navigates back
|
||||
final Account account = KeychainApplication.createAccountIfNecessary(getActivity());
|
||||
// for contacts sync
|
||||
initializeSyncCheckBox(
|
||||
(SwitchPreference) findPreference(Constants.Pref.SYNC_CONTACTS),
|
||||
account,
|
||||
ContactsContract.AUTHORITY
|
||||
);
|
||||
}
|
||||
|
||||
private void initializeSyncCheckBox(final SwitchPreference syncCheckBox,
|
||||
final Account account,
|
||||
final String authority) {
|
||||
// account is null if it could not be created for some reason
|
||||
boolean syncEnabled =
|
||||
account != null
|
||||
&& ContentResolver.getSyncAutomatically(account, authority)
|
||||
&& checkContactsPermission(authority);
|
||||
syncCheckBox.setChecked(syncEnabled);
|
||||
setSummary(syncCheckBox, authority, syncEnabled);
|
||||
|
||||
syncCheckBox.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean syncEnabled = (Boolean) newValue;
|
||||
if (syncEnabled) {
|
||||
if (checkContactsPermission(authority)) {
|
||||
ContentResolver.setSyncAutomatically(account, authority, true);
|
||||
setSummary(syncCheckBox, authority, true);
|
||||
return true;
|
||||
} else {
|
||||
requestPermissions(
|
||||
new String[]{Manifest.permission.READ_CONTACTS},
|
||||
REQUEST_PERMISSION_READ_CONTACTS);
|
||||
// don't update preference
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (account == null) {
|
||||
// if account could not be created for some reason,
|
||||
// we can't have our sync
|
||||
return false;
|
||||
}
|
||||
// disable syncs
|
||||
ContentResolver.setSyncAutomatically(account, authority, false);
|
||||
// cancel any ongoing/pending syncs
|
||||
ContentResolver.cancelSync(account, authority);
|
||||
setSummary(syncCheckBox, authority, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkContactsPermission(String authority) {
|
||||
if (!ContactsContract.AUTHORITY.equals(authority)) {
|
||||
// provides convenience of not using separate checks for keyserver and contact sync
|
||||
// in initializeSyncCheckBox
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_CONTACTS)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
|
||||
if (requestCode != REQUEST_PERMISSION_READ_CONTACTS) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean permissionWasGranted = grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (permissionWasGranted) {
|
||||
// permission granted -> enable contact linking
|
||||
AccountManager manager = AccountManager.get(getActivity());
|
||||
final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
|
||||
SwitchPreference pref = (SwitchPreference) findPreference(Constants.Pref.SYNC_CONTACTS);
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
setSummary(pref, ContactsContract.AUTHORITY, true);
|
||||
pref.setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSummary(SwitchPreference syncCheckBox, String authority,
|
||||
boolean checked) {
|
||||
switch (authority) {
|
||||
case Constants.PROVIDER_AUTHORITY: {
|
||||
if (checked) {
|
||||
syncCheckBox.setSummary(R.string.label_sync_settings_keyserver_summary_on);
|
||||
} else {
|
||||
syncCheckBox.setSummary(R.string.label_sync_settings_keyserver_summary_off);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ContactsContract.AUTHORITY: {
|
||||
if (checked) {
|
||||
syncCheckBox.setSummary(R.string.label_sync_settings_contacts_summary_on);
|
||||
} else {
|
||||
syncCheckBox.setSummary(R.string.label_sync_settings_contacts_summary_off);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -579,14 +441,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
Activity activity = getActivity();
|
||||
((KeychainApplication) activity.getApplication()).getAnalyticsManager().refreshSettings(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
|
|
|
@ -22,17 +22,9 @@ import java.util.List;
|
|||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
|
||||
import android.view.ActionMode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -41,16 +33,22 @@ import android.view.View;
|
|||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
|
||||
import com.astuetz.PagerSlidingTabStrip;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.livedata.GenericLiveData;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.model.UserPacket.UserId;
|
||||
import org.sufficientlysecure.keychain.analytics.AnalyticsManager;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
|
||||
|
@ -89,8 +87,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList
|
|||
}
|
||||
}
|
||||
|
||||
private AnalyticsManager analyticsManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -98,7 +94,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList
|
|||
setFullScreenDialogClose(v -> finish());
|
||||
|
||||
keyRepository = KeyRepository.create(this);
|
||||
analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager();
|
||||
|
||||
viewPager = findViewById(R.id.pager);
|
||||
slidingTabLayout = findViewById(R.id.sliding_tab_layout);
|
||||
|
@ -300,9 +295,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList
|
|||
actionMode = null;
|
||||
}
|
||||
invalidateOptionsMenu();
|
||||
|
||||
String fragmentName = tabAdapter.getItem(position).getClass().getSimpleName();
|
||||
analyticsManager.trackFragmentImpression(getClass().getSimpleName(), fragmentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,8 @@ import android.os.Bundle;
|
|||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -38,7 +40,6 @@ import android.widget.TextView;
|
|||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
|
@ -51,7 +52,7 @@ public class AddEmailDialogFragment extends DialogFragment implements OnEditorAc
|
|||
public static final String MESSAGE_DATA_EMAIL = "email";
|
||||
|
||||
private Messenger mMessenger;
|
||||
private EmailEditText mEmail;
|
||||
private AppCompatEditText mEmail;
|
||||
|
||||
public static AddEmailDialogFragment newInstance(Messenger messenger) {
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
@ -27,7 +27,6 @@ import android.os.Bundle;
|
|||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -37,11 +36,12 @@ import android.widget.Button;
|
|||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
|
||||
import org.sufficientlysecure.keychain.ui.widget.NameEditText;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
|
@ -55,8 +55,8 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
|
|||
public static final String MESSAGE_DATA_USER_ID = "user_id";
|
||||
|
||||
private Messenger mMessenger;
|
||||
private NameEditText mName;
|
||||
private EmailEditText mEmail;
|
||||
private AppCompatEditText mName;
|
||||
private AppCompatEditText mEmail;
|
||||
|
||||
public static AddUserIdDialogFragment newInstance(Messenger messenger, String predefinedName) {
|
||||
|
||||
|
|
|
@ -16,14 +16,11 @@ import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao;
|
|||
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
|
||||
|
||||
|
||||
public class KeyFragmentViewModel extends ViewModel {
|
||||
private LiveData<List<IdentityInfo>> identityInfo;
|
||||
private LiveData<KeySubkeyStatus> subkeyStatus;
|
||||
private LiveData<SystemContactInfo> systemContactInfo;
|
||||
private LiveData<KeyMetadata> keyserverStatus;
|
||||
|
||||
LiveData<List<IdentityInfo>> getIdentityInfo(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData) {
|
||||
|
@ -46,17 +43,6 @@ public class KeyFragmentViewModel extends ViewModel {
|
|||
return subkeyStatus;
|
||||
}
|
||||
|
||||
LiveData<SystemContactInfo> getSystemContactInfo(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData) {
|
||||
if (systemContactInfo == null) {
|
||||
SystemContactDao systemContactDao = SystemContactDao.getInstance(context);
|
||||
systemContactInfo = Transformations.switchMap(unifiedKeyInfoLiveData,
|
||||
(unifiedKeyInfo) -> unifiedKeyInfo == null ? null : new GenericLiveData<>(context,
|
||||
() -> systemContactDao.getSystemContactInfo(unifiedKeyInfo.master_key_id(),
|
||||
unifiedKeyInfo.has_any_secret())));
|
||||
}
|
||||
return systemContactInfo;
|
||||
}
|
||||
|
||||
LiveData<KeyMetadata> getKeyserverStatus(Context context, LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData) {
|
||||
if (keyserverStatus == null) {
|
||||
KeyMetadataDao keyMetadataDao = KeyMetadataDao.create(context);
|
||||
|
|
|
@ -28,11 +28,9 @@ import android.animation.ObjectAnimator;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
|
@ -40,17 +38,6 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.provider.ContactsContract;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -58,13 +45,22 @@ import android.view.animation.AlphaAnimation;
|
|||
import android.view.animation.Animation;
|
||||
import android.view.animation.Animation.AnimationListener;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
|
@ -102,10 +98,8 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
|||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.ShareKeyHelper;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
||||
|
@ -121,7 +115,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
|
||||
public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
|
||||
public static final String EXTRA_DISPLAY_RESULT = "display_result";
|
||||
public static final String EXTRA_LINKED_TRANSITION = "linked_transition";
|
||||
|
||||
KeyRepository keyRepository;
|
||||
|
||||
|
@ -140,8 +133,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
private ImageButton actionShare;
|
||||
private ImageButton actionShareClipboard;
|
||||
private FloatingActionButton floatingActionButton;
|
||||
private ImageView photoView;
|
||||
private FrameLayout photoLayout;
|
||||
private ImageView qrCodeView;
|
||||
private CardView qrCodeLayout;
|
||||
|
||||
|
@ -181,8 +172,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
actionShare= findViewById(R.id.view_key_action_share);
|
||||
actionShareClipboard = findViewById(R.id.view_key_action_share_clipboard);
|
||||
floatingActionButton = findViewById(R.id.fab);
|
||||
photoView = findViewById(R.id.view_key_photo);
|
||||
photoLayout = findViewById(R.id.view_key_photo_layout);
|
||||
qrCodeView = findViewById(R.id.view_key_qr_code);
|
||||
qrCodeLayout = findViewById(R.id.view_key_qr_code_layout);
|
||||
|
||||
|
@ -243,20 +232,10 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
|
||||
long masterKeyId;
|
||||
Intent intent = getIntent();
|
||||
Uri dataUri = intent.getData();
|
||||
if (intent.hasExtra(EXTRA_MASTER_KEY_ID)) {
|
||||
masterKeyId = intent.getLongExtra(EXTRA_MASTER_KEY_ID, 0L);
|
||||
} else if (dataUri != null && dataUri.getHost().equals(ContactsContract.AUTHORITY)) {
|
||||
Long contactMasterKeyId = new ContactHelper(this).masterKeyIdFromContactsDataUri(dataUri);
|
||||
if (contactMasterKeyId == null) {
|
||||
Timber.e("Contact Data missing. Should be uri of key!");
|
||||
Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
masterKeyId = contactMasterKeyId;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Missing required extra master_key_id or contact uri");
|
||||
throw new IllegalArgumentException("Missing required extra master_key_id");
|
||||
}
|
||||
|
||||
actionEncryptFile.setOnClickListener(v -> encrypt(false));
|
||||
|
@ -660,25 +639,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
// this is done at the end of the animation otherwise
|
||||
}
|
||||
|
||||
AsyncTask<Long, Void, Bitmap> photoTask =
|
||||
new AsyncTask<Long, Void, Bitmap>() {
|
||||
protected Bitmap doInBackground(Long... mMasterKeyId) {
|
||||
return new ContactHelper(ViewKeyActivity.this)
|
||||
.loadPhotoByMasterKeyId(mMasterKeyId[0], true);
|
||||
}
|
||||
|
||||
protected void onPostExecute(Bitmap photo) {
|
||||
if (photo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
photoView.setImageBitmap(photo);
|
||||
photoView.setColorFilter(ContextCompat.getColor(ViewKeyActivity.this, R.color.toolbar_photo_tint),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
photoLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
};
|
||||
|
||||
boolean showStatusText = unifiedKeyInfo.is_secure() && !unifiedKeyInfo.is_expired() && !unifiedKeyInfo.is_revoked();
|
||||
if (showStatusText) {
|
||||
statusText.setVisibility(View.VISIBLE);
|
||||
|
@ -733,7 +693,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
if (!Arrays.equals(unifiedKeyInfo.fingerprint(), qrCodeLoaded)) {
|
||||
loadQrCode(unifiedKeyInfo.fingerprint());
|
||||
}
|
||||
photoTask.execute(unifiedKeyInfo.master_key_id());
|
||||
qrCodeLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
// and place leftOf qr code
|
||||
|
@ -775,7 +734,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity {
|
|||
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
|
||||
State.VERIFIED, R.color.icons, true);
|
||||
color = ContextCompat.getColor(this, R.color.key_flag_green);
|
||||
photoTask.execute(unifiedKeyInfo.master_key_id());
|
||||
|
||||
hideFab();
|
||||
} else {
|
||||
|
|
|
@ -20,25 +20,21 @@ package org.sufficientlysecure.keychain.ui.keyview;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.ContactsContract;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||
import org.sufficientlysecure.keychain.daos.AutocryptPeerDao;
|
||||
|
@ -55,18 +51,15 @@ import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo;
|
|||
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeyHealthStatus;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.view.IdentitiesCardView;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.view.KeyHealthView;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.view.KeyStatusList.KeyDisplayStatus;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusView;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener {
|
||||
private IdentitiesCardView identitiesCardView;
|
||||
private SystemContactCardView systemContactCard;
|
||||
private KeyHealthView keyStatusHealth;
|
||||
private KeyserverStatusView keyserverStatusView;
|
||||
private View keyStatusCardView;
|
||||
|
@ -87,7 +80,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener
|
|||
View view = inflater.inflate(R.layout.view_key_fragment, viewGroup, false);
|
||||
|
||||
identitiesCardView = view.findViewById(R.id.card_identities);
|
||||
systemContactCard = view.findViewById(R.id.linked_system_contact_card);
|
||||
keyStatusCardView = view.findViewById(R.id.subkey_status_card);
|
||||
keyStatusHealth = view.findViewById(R.id.key_status_health);
|
||||
keyserverStatusView = view.findViewById(R.id.key_status_keyserver);
|
||||
|
@ -118,17 +110,16 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener
|
|||
|
||||
Context context = requireContext();
|
||||
|
||||
UnifiedKeyInfoViewModel viewKeyViewModel = ViewModelProviders.of(requireActivity()).get(UnifiedKeyInfoViewModel.class);
|
||||
UnifiedKeyInfoViewModel viewKeyViewModel = new ViewModelProvider(requireActivity()).get(UnifiedKeyInfoViewModel.class);
|
||||
LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData = viewKeyViewModel.getUnifiedKeyInfoLiveData(requireContext());
|
||||
|
||||
unifiedKeyInfoLiveData.observe(this, this::onLoadUnifiedKeyInfo);
|
||||
unifiedKeyInfoLiveData.observe(getViewLifecycleOwner(), this::onLoadUnifiedKeyInfo);
|
||||
|
||||
KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class);
|
||||
KeyFragmentViewModel model = new ViewModelProvider(this).get(KeyFragmentViewModel.class);
|
||||
|
||||
model.getIdentityInfo(context, unifiedKeyInfoLiveData).observe(this, this::onLoadIdentityInfo);
|
||||
model.getKeyserverStatus(context, unifiedKeyInfoLiveData).observe(this, this::onLoadKeyMetadata);
|
||||
model.getSystemContactInfo(context, unifiedKeyInfoLiveData).observe(this, this::onLoadSystemContact);
|
||||
model.getSubkeyStatus(context, unifiedKeyInfoLiveData).observe(this, this::onLoadSubkeyStatus);
|
||||
model.getIdentityInfo(context, unifiedKeyInfoLiveData).observe(getViewLifecycleOwner(), this::onLoadIdentityInfo);
|
||||
model.getKeyserverStatus(context, unifiedKeyInfoLiveData).observe(getViewLifecycleOwner(), this::onLoadKeyMetadata);
|
||||
model.getSubkeyStatus(context, unifiedKeyInfoLiveData).observe(getViewLifecycleOwner(), this::onLoadSubkeyStatus);
|
||||
}
|
||||
|
||||
private void onLoadSubkeyStatus(KeySubkeyStatus subkeyStatus) {
|
||||
|
@ -274,16 +265,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener
|
|||
identitiesAdapter.setData(identityInfos);
|
||||
}
|
||||
|
||||
private void onLoadSystemContact(SystemContactInfo systemContactInfo) {
|
||||
if (systemContactInfo == null) {
|
||||
systemContactCard.hideLinkedSystemContact();
|
||||
return;
|
||||
}
|
||||
|
||||
systemContactCard.showLinkedSystemContact(systemContactInfo.contactName, systemContactInfo.contactPicture);
|
||||
systemContactCard.setSystemContactClickListener((v) -> launchAndroidContactActivity(systemContactInfo.contactId));
|
||||
}
|
||||
|
||||
private void onLoadKeyMetadata(KeyMetadata keyMetadata) {
|
||||
if (keyMetadata == null) {
|
||||
keyserverStatusView.setDisplayStatusUnknown();
|
||||
|
@ -299,14 +280,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener
|
|||
}
|
||||
}
|
||||
|
||||
public void switchToFragment(final Fragment frag, final String backStackName) {
|
||||
new Handler().post(() -> requireFragmentManager().beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.replace(R.id.view_key_fragment, frag)
|
||||
.addToBackStack(backStackName)
|
||||
.commit());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// if a result has been returned, display a notify
|
||||
|
@ -320,7 +293,7 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener
|
|||
|
||||
public void showDialogFragment(final DialogFragment dialogFragment, final String tag) {
|
||||
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(
|
||||
() -> dialogFragment.show(requireFragmentManager(), tag));
|
||||
() -> dialogFragment.show(getParentFragmentManager(), tag));
|
||||
}
|
||||
|
||||
public void showContextMenu(int position, View anchor) {
|
||||
|
@ -349,11 +322,4 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void launchAndroidContactActivity(long contactId) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));
|
||||
intent.setData(uri);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.ui.keyview.loader;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class SystemContactDao {
|
||||
private static final String[] PROJECTION = {
|
||||
ContactsContract.RawContacts.CONTACT_ID
|
||||
};
|
||||
private static final int INDEX_CONTACT_ID = 0;
|
||||
private static final String CONTACT_NOT_DELETED = "0";
|
||||
|
||||
|
||||
private final Context context;
|
||||
private final ContentResolver contentResolver;
|
||||
private final ContactHelper contactHelper;
|
||||
|
||||
|
||||
public static SystemContactDao getInstance(Context context) {
|
||||
ContactHelper contactHelper = new ContactHelper(context);
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
return new SystemContactDao(context, contactHelper, contentResolver);
|
||||
}
|
||||
|
||||
private SystemContactDao(Context context, ContactHelper contactHelper, ContentResolver contentResolver) {
|
||||
this.context = context;
|
||||
this.contactHelper = contactHelper;
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
public SystemContactInfo getSystemContactInfo(long masterKeyId, boolean isSecret) {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
|
||||
== PackageManager.PERMISSION_DENIED) {
|
||||
Timber.w(Constants.TAG, "loading linked system contact not possible READ_CONTACTS permission denied!");
|
||||
return null;
|
||||
}
|
||||
|
||||
Uri baseUri = isSecret ? ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI :
|
||||
ContactsContract.RawContacts.CONTENT_URI;
|
||||
Cursor cursor = contentResolver.query(baseUri, PROJECTION,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + " = ? AND " +
|
||||
ContactsContract.RawContacts.SOURCE_ID + " = ? AND " +
|
||||
ContactsContract.RawContacts.DELETED + " = ?",
|
||||
new String[] {
|
||||
Constants.ACCOUNT_TYPE,
|
||||
Long.toString(masterKeyId),
|
||||
CONTACT_NOT_DELETED
|
||||
}, null);
|
||||
|
||||
if (cursor == null) {
|
||||
Timber.e("Error loading key items!");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!cursor.moveToFirst()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long contactId = cursor.getLong(INDEX_CONTACT_ID);
|
||||
if (contactId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String contactName = null;
|
||||
if (isSecret) { //all secret keys are linked to "me" profile in contacts
|
||||
List<String> mainProfileNames = contactHelper.getMainProfileContactName();
|
||||
if (mainProfileNames != null && mainProfileNames.size() > 0) {
|
||||
contactName = mainProfileNames.get(0);
|
||||
}
|
||||
} else {
|
||||
contactName = contactHelper.getContactName(contactId);
|
||||
}
|
||||
|
||||
if (contactName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap contactPicture;
|
||||
if (isSecret) {
|
||||
contactPicture = contactHelper.loadMainProfilePhoto(false);
|
||||
} else {
|
||||
contactPicture = contactHelper.loadPhotoByContactId(contactId, false);
|
||||
}
|
||||
|
||||
return new SystemContactInfo(masterKeyId, contactId, contactName, contactPicture);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SystemContactInfo {
|
||||
final long masterKeyId;
|
||||
public final long contactId;
|
||||
public final String contactName;
|
||||
public final Bitmap contactPicture;
|
||||
|
||||
SystemContactInfo(long masterKeyId, long contactId, String contactName, Bitmap contactPicture) {
|
||||
this.masterKeyId = masterKeyId;
|
||||
this.contactId = contactId;
|
||||
this.contactName = contactName;
|
||||
this.contactPicture = contactPicture;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.ui.keyview.view;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
|
||||
public class SystemContactCardView extends CardView {
|
||||
private LinearLayout vSystemContactLayout;
|
||||
private ImageView vSystemContactPicture;
|
||||
private TextView vSystemContactName;
|
||||
|
||||
public SystemContactCardView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.system_contact_card, this, true);
|
||||
|
||||
vSystemContactLayout = view.findViewById(R.id.system_contact_layout);
|
||||
vSystemContactName = view.findViewById(R.id.system_contact_name);
|
||||
vSystemContactPicture = view.findViewById(R.id.system_contact_picture);
|
||||
}
|
||||
|
||||
public void setSystemContactClickListener(OnClickListener onClickListener) {
|
||||
vSystemContactLayout.setOnClickListener(onClickListener);
|
||||
}
|
||||
|
||||
public void hideLinkedSystemContact() {
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void showLinkedSystemContact(String contactName, Bitmap picture) {
|
||||
vSystemContactName.setText(contactName);
|
||||
if (picture != null) {
|
||||
vSystemContactPicture.setImageBitmap(picture);
|
||||
} else {
|
||||
vSystemContactPicture.setImageResource(R.drawable.ic_person_grey_48dp);
|
||||
}
|
||||
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
|
||||
public class EmailEditText extends AppCompatAutoCompleteTextView {
|
||||
|
||||
public EmailEditText(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public EmailEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public EmailEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
reenableKeyboardSuggestions();
|
||||
|
||||
addTextChangedListener(textWatcher);
|
||||
initAdapter();
|
||||
}
|
||||
|
||||
TextWatcher textWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private void initAdapter() {
|
||||
setThreshold(1); // Start working from first character
|
||||
setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item,
|
||||
new ContactHelper(getContext()).getPossibleUserEmails()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack to re-enable keyboard auto correction in AutoCompleteTextView.
|
||||
* From http://stackoverflow.com/a/22512858
|
||||
*/
|
||||
private void reenableKeyboardSuggestions() {
|
||||
int inputType = getInputType();
|
||||
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
|
||||
setRawInputType(inputType);
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
|
||||
public class NameEditText extends AppCompatAutoCompleteTextView {
|
||||
public NameEditText(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public NameEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public NameEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
reenableKeyboardSuggestions();
|
||||
initAdapter();
|
||||
}
|
||||
|
||||
private void initAdapter() {
|
||||
setThreshold(1); // Start working from first character
|
||||
setAdapter(new ArrayAdapter<>(
|
||||
getContext(), android.R.layout.simple_spinner_dropdown_item,
|
||||
new ContactHelper(getContext()).getPossibleUserNames()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack to re-enable keyboard suggestions in AutoCompleteTextView.
|
||||
* From http://stackoverflow.com/a/22512858
|
||||
*/
|
||||
private void reenableKeyboardSuggestions() {
|
||||
int inputType = getInputType();
|
||||
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
|
||||
setRawInputType(inputType);
|
||||
}
|
||||
}
|
|
@ -1,850 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Schürmann & Breitmoser GbR
|
||||
*
|
||||
* 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 org.sufficientlysecure.keychain.util;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Im;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.Patterns;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.model.UserPacket.UserId;
|
||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ContactHelper {
|
||||
private final KeyRepository keyRepository;
|
||||
|
||||
private Context mContext;
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
public ContactHelper(Context context) {
|
||||
mContext = context;
|
||||
mContentResolver = context.getContentResolver();
|
||||
keyRepository = KeyRepository.create(context);
|
||||
}
|
||||
|
||||
public List<String> getPossibleUserEmails() {
|
||||
Set<String> accountMails = getAccountEmails();
|
||||
accountMails.addAll(getMainProfileContactEmails());
|
||||
|
||||
// remove items that are not an email
|
||||
Iterator<String> it = accountMails.iterator();
|
||||
while (it.hasNext()) {
|
||||
String email = it.next();
|
||||
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
|
||||
if (!emailMatcher.matches()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// now return the Set (without duplicates) as a List
|
||||
return new ArrayList<>(accountMails);
|
||||
}
|
||||
|
||||
public List<String> getPossibleUserNames() {
|
||||
Set<String> accountMails = getAccountEmails();
|
||||
Set<String> names = getContactNamesFromEmails(accountMails);
|
||||
names.addAll(getMainProfileContactName());
|
||||
|
||||
// remove items that are an email
|
||||
Iterator<String> it = names.iterator();
|
||||
while (it.hasNext()) {
|
||||
String email = it.next();
|
||||
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
|
||||
if (emailMatcher.matches()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get emails from AccountManager
|
||||
*/
|
||||
private Set<String> getAccountEmails() {
|
||||
final Account[] accounts = AccountManager.get(mContext).getAccounts();
|
||||
final Set<String> emailSet = new HashSet<>();
|
||||
for (Account account : accounts) {
|
||||
emailSet.add(account.name);
|
||||
}
|
||||
return emailSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for contact names based on a list of emails (to find out the names of the
|
||||
* device owner based on the email addresses from AccountsManager)
|
||||
*/
|
||||
private Set<String> getContactNamesFromEmails(Set<String> emails) {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
Set<String> names = new HashSet<>();
|
||||
for (String email : emails) {
|
||||
Cursor profileCursor = mContentResolver.query(
|
||||
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.CommonDataKinds.Email.ADDRESS,
|
||||
ContactsContract.Contacts.DISPLAY_NAME
|
||||
},
|
||||
ContactsContract.CommonDataKinds.Email.ADDRESS + "=?",
|
||||
new String[]{email}, null
|
||||
);
|
||||
if (profileCursor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Set<String> currNames = new HashSet<>();
|
||||
while (profileCursor.moveToNext()) {
|
||||
String name = profileCursor.getString(1);
|
||||
if (name != null) {
|
||||
currNames.add(name);
|
||||
}
|
||||
}
|
||||
profileCursor.close();
|
||||
names.addAll(currNames);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the emails of the primary profile contact
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*/
|
||||
private Set<String> getMainProfileContactEmails() {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
Cursor profileCursor = mContentResolver.query(
|
||||
Uri.withAppendedPath(
|
||||
ContactsContract.Profile.CONTENT_URI,
|
||||
ContactsContract.Contacts.Data.CONTENT_DIRECTORY),
|
||||
new String[]{
|
||||
ContactsContract.CommonDataKinds.Email.ADDRESS,
|
||||
ContactsContract.CommonDataKinds.Email.IS_PRIMARY
|
||||
},
|
||||
// Selects only email addresses
|
||||
ContactsContract.Contacts.Data.MIMETYPE + "=?",
|
||||
new String[]{
|
||||
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
|
||||
},
|
||||
// Show primary rows first. Note that there won't be a primary email address if the
|
||||
// user hasn't specified one.
|
||||
ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"
|
||||
);
|
||||
if (profileCursor == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
Set<String> emails = new HashSet<>();
|
||||
while (profileCursor.moveToNext()) {
|
||||
String email = profileCursor.getString(0);
|
||||
if (email != null) {
|
||||
emails.add(email);
|
||||
}
|
||||
}
|
||||
profileCursor.close();
|
||||
return emails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the name of the primary profile contact
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*/
|
||||
public List<String> getMainProfileContactName() {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Cursor profileCursor = mContentResolver.query(
|
||||
ContactsContract.Profile.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.Profile.DISPLAY_NAME
|
||||
},
|
||||
null, null, null);
|
||||
if (profileCursor == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<String> names = new HashSet<>();
|
||||
// should only contain one entry!
|
||||
while (profileCursor.moveToNext()) {
|
||||
String name = profileCursor.getString(0);
|
||||
if (name != null) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
profileCursor.close();
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the CONTACT_ID of the main ("me") contact
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*/
|
||||
private long getMainProfileContactId() {
|
||||
Cursor profileCursor = mContentResolver.query(ContactsContract.Profile.CONTENT_URI,
|
||||
new String[]{ContactsContract.Profile._ID}, null, null, null);
|
||||
|
||||
if (profileCursor != null && profileCursor.getCount() != 0 && profileCursor.moveToNext()) {
|
||||
long contactId = profileCursor.getLong(0);
|
||||
profileCursor.close();
|
||||
return contactId;
|
||||
} else {
|
||||
if (profileCursor != null) {
|
||||
profileCursor.close();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* loads the profile picture of the main ("me") contact
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*
|
||||
* @param highRes true for large image if present, false for thumbnail
|
||||
* @return bitmap of loaded photo
|
||||
*/
|
||||
public Bitmap loadMainProfilePhoto(boolean highRes) {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
long mainProfileContactId = getMainProfileContactId();
|
||||
|
||||
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
|
||||
Long.toString(mainProfileContactId));
|
||||
InputStream photoInputStream = ContactsContract.Contacts.openContactPhotoInputStream(
|
||||
mContentResolver,
|
||||
contactUri,
|
||||
highRes
|
||||
);
|
||||
if (photoInputStream == null) {
|
||||
return null;
|
||||
}
|
||||
return BitmapFactory.decodeStream(photoInputStream);
|
||||
} catch (Throwable ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getContactMails() {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Cursor mailCursor = mContentResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
|
||||
new String[]{ContactsContract.CommonDataKinds.Email.DATA},
|
||||
null, null, null);
|
||||
if (mailCursor == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<String> mails = new HashSet<>();
|
||||
while (mailCursor.moveToNext()) {
|
||||
String email = mailCursor.getString(0);
|
||||
if (email != null) {
|
||||
mails.add(email);
|
||||
}
|
||||
}
|
||||
mailCursor.close();
|
||||
return new ArrayList<>(mails);
|
||||
}
|
||||
|
||||
public List<String> getContactNames() {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Cursor cursor = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI,
|
||||
new String[]{ContactsContract.Contacts.DISPLAY_NAME},
|
||||
null, null, null);
|
||||
if (cursor == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Set<String> names = new HashSet<>();
|
||||
while (cursor.moveToNext()) {
|
||||
String name = cursor.getString(0);
|
||||
if (name != null) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
public Long masterKeyIdFromContactsDataUri(Uri contactUri) {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Cursor contactMasterKey = mContentResolver.query(contactUri,
|
||||
new String[]{ContactsContract.Data.DATA2}, null, null, null);
|
||||
if (contactMasterKey != null) {
|
||||
try {
|
||||
if (contactMasterKey.moveToNext()) {
|
||||
return contactMasterKey.getLong(0);
|
||||
}
|
||||
} finally {
|
||||
contactMasterKey.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the CONTACT_ID of the raw contact to which a masterKeyId is associated, if the
|
||||
* raw contact has not been marked for deletion.
|
||||
*
|
||||
* @return CONTACT_ID (id of aggregated contact) linked to masterKeyId
|
||||
*/
|
||||
private long findContactId(long masterKeyId) {
|
||||
long contactId = -1;
|
||||
Cursor raw = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.RawContacts.CONTACT_ID
|
||||
},
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
|
||||
ContactsContract.RawContacts.SOURCE_ID + "=? AND " +
|
||||
ContactsContract.RawContacts.DELETED + "=?",
|
||||
new String[]{//"0" for "not deleted"
|
||||
Constants.ACCOUNT_TYPE,
|
||||
Long.toString(masterKeyId),
|
||||
"0"
|
||||
}, null);
|
||||
if (raw != null) {
|
||||
if (raw.moveToNext()) {
|
||||
contactId = raw.getLong(0);
|
||||
}
|
||||
raw.close();
|
||||
}
|
||||
return contactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of the system contact associated with contactId, null if the
|
||||
* contact does not exist
|
||||
*
|
||||
* @return primary display name of system contact associated with contactId, null if it does
|
||||
* not exist
|
||||
*/
|
||||
public String getContactName(long contactId) {
|
||||
String contactName = null;
|
||||
Cursor raw = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
|
||||
},
|
||||
ContactsContract.Contacts._ID + "=?",
|
||||
new String[]{//"0" for "not deleted"
|
||||
Long.toString(contactId)
|
||||
}, null);
|
||||
if (raw != null) {
|
||||
if (raw.moveToNext()) {
|
||||
contactName = raw.getString(0);
|
||||
}
|
||||
raw.close();
|
||||
}
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public Bitmap loadPhotoByMasterKeyId(long masterKeyId, boolean highRes) {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (masterKeyId == -1) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
long contactId = findContactId(masterKeyId);
|
||||
return loadPhotoByContactId(contactId, highRes);
|
||||
|
||||
} catch (Throwable ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap loadPhotoByContactId(long contactId, boolean highRes) {
|
||||
if (!isContactsPermissionGranted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (contactId == -1) {
|
||||
return null;
|
||||
}
|
||||
Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
|
||||
// older android versions (tested on API level 15) fail on lookupuris being passed to
|
||||
// openContactPhotoInputStream
|
||||
// http://stackoverflow.com/a/21214524/3000919
|
||||
// Uri lookupUri = ContactsContract.Contacts.getLookupUri(contentResolver, contactUri);
|
||||
// Also, we don't need a permanent shortcut to the contact since we load it afresh each time
|
||||
|
||||
InputStream photoInputStream = ContactsContract.Contacts.openContactPhotoInputStream(
|
||||
mContentResolver,
|
||||
contactUri,
|
||||
highRes);
|
||||
|
||||
if (photoInputStream == null) {
|
||||
return null;
|
||||
}
|
||||
return BitmapFactory.decodeStream(photoInputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write/Update the current OpenKeychain keys to the contact db
|
||||
*/
|
||||
public void writeKeysToContacts() {
|
||||
if (Constants.DEBUG_SYNC_REMOVE_CONTACTS) {
|
||||
deleteAllContacts();
|
||||
}
|
||||
|
||||
writeKeysToMainProfileContact();
|
||||
|
||||
writeKeysToNormalContacts();
|
||||
}
|
||||
|
||||
private boolean isContactsPermissionGranted() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_CONTACTS)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Timber.w("READ_CONTACTS permission denied!");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void writeKeysToNormalContacts() {
|
||||
// delete raw contacts flagged for deletion by user so they can be reinserted
|
||||
deleteFlaggedNormalRawContacts();
|
||||
|
||||
Set<Long> deletedKeys = getRawContactMasterKeyIds();
|
||||
|
||||
// Load all public Keys from OK
|
||||
for (UnifiedKeyInfo keyInfo : keyRepository.getAllUnifiedKeyInfo()) {
|
||||
if (keyInfo.has_any_secret()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long masterKeyId = keyInfo.master_key_id();
|
||||
String name = keyInfo.name();
|
||||
|
||||
deletedKeys.remove(masterKeyId);
|
||||
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
||||
|
||||
// Do not store expired or revoked or unverified keys in contact db - and
|
||||
// remove them if they already exist. Secret keys do not reach this point
|
||||
if (keyInfo.is_expired() || keyInfo.is_revoked() || !keyInfo.is_verified()) {
|
||||
Timber.d("Expired or revoked or unverified: Deleting masterKeyId "
|
||||
+ masterKeyId);
|
||||
if (masterKeyId != -1) {
|
||||
deleteRawContactByMasterKeyId(masterKeyId);
|
||||
}
|
||||
} else {
|
||||
if (name != null) {
|
||||
|
||||
// get raw contact to this master key id
|
||||
long rawContactId = findRawContactId(masterKeyId);
|
||||
Timber.d("rawContactId: " + rawContactId);
|
||||
|
||||
// Create a new rawcontact with corresponding key if it does not exist yet
|
||||
if (rawContactId == -1) {
|
||||
Timber.d("Insert new raw contact with masterKeyId " + masterKeyId);
|
||||
|
||||
insertContact(ops, masterKeyId);
|
||||
writeContactKey(ops, rawContactId, masterKeyId, name);
|
||||
}
|
||||
|
||||
// We always update the display name (which is derived from primary user id)
|
||||
// and email addresses from user id
|
||||
writeContactDisplayName(ops, rawContactId, name);
|
||||
writeContactEmail(ops, rawContactId, masterKeyId);
|
||||
try {
|
||||
mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
} catch (Exception e) {
|
||||
Timber.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete master key ids that are no longer present in OK
|
||||
for (Long masterKeyId : deletedKeys) {
|
||||
Timber.d("Delete raw contact with masterKeyId " + masterKeyId);
|
||||
deleteRawContactByMasterKeyId(masterKeyId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Links all keys with secrets to the main ("me") contact
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*/
|
||||
private void writeKeysToMainProfileContact() {
|
||||
// deletes contacts hidden by the user so they can be reinserted if necessary
|
||||
deleteFlaggedMainProfileRawContacts();
|
||||
|
||||
Set<Long> keysToDelete = getMainProfileMasterKeyIds();
|
||||
|
||||
// get all keys which have associated secret keys
|
||||
// TODO: figure out why using selectionArgs does not work in this case
|
||||
for (UnifiedKeyInfo keyInfo : keyRepository.getAllUnifiedKeyInfoWithSecret()) {
|
||||
long masterKeyId = keyInfo.master_key_id();
|
||||
|
||||
if (!keyInfo.is_expired() && !keyInfo.is_revoked() && keyInfo.name() != null) {
|
||||
// if expired or revoked will not be removed from keysToDelete or inserted
|
||||
// into main profile ("me" contact)
|
||||
boolean existsInMainProfile = keysToDelete.remove(masterKeyId);
|
||||
if (!existsInMainProfile) {
|
||||
long rawContactId = -1;//new raw contact
|
||||
|
||||
Timber.d("masterKeyId with secret " + masterKeyId);
|
||||
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
||||
insertMainProfileRawContact(ops, masterKeyId);
|
||||
writeContactKey(ops, rawContactId, masterKeyId, keyInfo.name());
|
||||
|
||||
try {
|
||||
mContentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
} catch (Exception e) {
|
||||
Timber.w(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (long masterKeyId : keysToDelete) {
|
||||
deleteMainProfileRawContactByMasterKeyId(masterKeyId);
|
||||
Timber.d("Delete main profile raw contact with masterKeyId " + masterKeyId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a raw contact into the table defined by ContactsContract.Profile
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*/
|
||||
private void insertMainProfileRawContact(ArrayList<ContentProviderOperation> ops,
|
||||
long masterKeyId) {
|
||||
ops.add(ContentProviderOperation.newInsert(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI)
|
||||
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME)
|
||||
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE)
|
||||
.withValue(ContactsContract.RawContacts.SOURCE_ID, Long.toString(masterKeyId))
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes a raw contact from the main profile table ("me" contact)
|
||||
* http://developer.android.com/reference/android/provider/ContactsContract.Profile.html
|
||||
*
|
||||
* @return number of rows deleted
|
||||
*/
|
||||
private int deleteMainProfileRawContactByMasterKeyId(long masterKeyId) {
|
||||
// CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
|
||||
// would be just flagged for deletion
|
||||
Uri deleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon().
|
||||
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
|
||||
|
||||
return mContentResolver.delete(deleteUri,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
|
||||
ContactsContract.RawContacts.SOURCE_ID + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes all raw contact entries in the "me" contact flagged for deletion ('hidden'),
|
||||
* presumably by the user
|
||||
*
|
||||
* @return number of raw contacts deleted
|
||||
*/
|
||||
private int deleteFlaggedMainProfileRawContacts() {
|
||||
// CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
|
||||
// would be just flagged for deletion
|
||||
Uri deleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon().
|
||||
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
|
||||
|
||||
return mContentResolver.delete(deleteUri,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
|
||||
ContactsContract.RawContacts.DELETED + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE,
|
||||
"1"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all raw contacts associated to OpenKeychain, including those from "me" contact
|
||||
* defined by ContactsContract.Profile
|
||||
*
|
||||
* @return number of rows deleted
|
||||
*/
|
||||
public int deleteAllContacts() {
|
||||
// CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
|
||||
// would be just flagged for deletion
|
||||
Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().
|
||||
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
|
||||
|
||||
Timber.d("Deleting all raw contacts associated to OK...");
|
||||
int delete = mContentResolver.delete(deleteUri,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE
|
||||
});
|
||||
|
||||
Uri mainProfileDeleteUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon()
|
||||
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
|
||||
|
||||
delete += mContentResolver.delete(mainProfileDeleteUri,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE
|
||||
});
|
||||
|
||||
return delete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes raw contacts from ContactsContract.RawContacts based on masterKeyId. Does not
|
||||
* delete contacts from the "me" contact defined in ContactsContract.Profile
|
||||
*
|
||||
* @return number of rows deleted
|
||||
*/
|
||||
private int deleteRawContactByMasterKeyId(long masterKeyId) {
|
||||
// CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
|
||||
// would be just flagged for deletion
|
||||
Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().
|
||||
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
|
||||
|
||||
return mContentResolver.delete(deleteUri,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
|
||||
ContactsContract.RawContacts.SOURCE_ID + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
|
||||
});
|
||||
}
|
||||
|
||||
private int deleteFlaggedNormalRawContacts() {
|
||||
// CALLER_IS_SYNCADAPTER allows us to actually wipe the RawContact from the device, otherwise
|
||||
// would be just flagged for deletion
|
||||
Uri deleteUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().
|
||||
appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
|
||||
|
||||
return mContentResolver.delete(deleteUri,
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " +
|
||||
ContactsContract.RawContacts.DELETED + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE,
|
||||
"1"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set of all key master key ids currently present in the contact db
|
||||
*/
|
||||
private Set<Long> getRawContactMasterKeyIds() {
|
||||
HashSet<Long> result = new HashSet<>();
|
||||
Cursor masterKeyIds = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.RawContacts.SOURCE_ID
|
||||
},
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE
|
||||
}, null);
|
||||
if (masterKeyIds != null) {
|
||||
while (masterKeyIds.moveToNext()) {
|
||||
result.add(masterKeyIds.getLong(0));
|
||||
}
|
||||
masterKeyIds.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set of all key master key ids currently present in the contact db
|
||||
*/
|
||||
private Set<Long> getMainProfileMasterKeyIds() {
|
||||
HashSet<Long> result = new HashSet<>();
|
||||
Cursor masterKeyIds = mContentResolver.query(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI,
|
||||
new String[]{
|
||||
ContactsContract.RawContacts.SOURCE_ID
|
||||
},
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE
|
||||
}, null);
|
||||
if (masterKeyIds != null) {
|
||||
while (masterKeyIds.moveToNext()) {
|
||||
result.add(masterKeyIds.getLong(0));
|
||||
}
|
||||
masterKeyIds.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will search the contact db for a raw contact with a given master key id
|
||||
*
|
||||
* @return raw contact id or -1 if not found
|
||||
*/
|
||||
private long findRawContactId(long masterKeyId) {
|
||||
long rawContactId = -1;
|
||||
Cursor raw = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI,
|
||||
new String[]{
|
||||
ContactsContract.RawContacts._ID
|
||||
},
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
|
||||
}, null);
|
||||
if (raw != null) {
|
||||
if (raw.moveToNext()) {
|
||||
rawContactId = raw.getLong(0);
|
||||
}
|
||||
raw.close();
|
||||
}
|
||||
return rawContactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a empty raw contact with a given masterKeyId
|
||||
*/
|
||||
private void insertContact(ArrayList<ContentProviderOperation> ops, long masterKeyId) {
|
||||
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
|
||||
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME)
|
||||
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE)
|
||||
.withValue(ContactsContract.RawContacts.SOURCE_ID, Long.toString(masterKeyId))
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key id to the given raw contact.
|
||||
* <p/>
|
||||
* This creates the link to OK in contact details
|
||||
*/
|
||||
private void writeContactKey(ArrayList<ContentProviderOperation> ops, long rawContactId,
|
||||
long masterKeyId, String keyName) {
|
||||
ops.add(referenceRawContact(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, Constants.CUSTOM_CONTACT_DATA_MIME_TYPE)
|
||||
.withValue(ContactsContract.Data.DATA1, mContext.getString(R.string.contact_show_key, keyName))
|
||||
.withValue(ContactsContract.Data.DATA2, masterKeyId)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all known email addresses of a key (derived from user ids) to a given raw contact
|
||||
*/
|
||||
private void writeContactEmail(ArrayList<ContentProviderOperation> ops,
|
||||
long rawContactId, long masterKeyId) {
|
||||
ContentProviderOperation deleteEmailOp = selectByRawContactAndItemType(
|
||||
ContentProviderOperation.newDelete(Data.CONTENT_URI), rawContactId, Email.CONTENT_ITEM_TYPE).build();
|
||||
ops.add(deleteEmailOp);
|
||||
|
||||
ContentProviderOperation deleteJabberOp = selectByRawContactAndItemType(
|
||||
ContentProviderOperation.newDelete(Data.CONTENT_URI), rawContactId, Im.CONTENT_ITEM_TYPE).build();
|
||||
ops.add(deleteJabberOp);
|
||||
|
||||
for (UserId userId : keyRepository.getUserIds(masterKeyId)) {
|
||||
if (userId.email() != null) {
|
||||
ContentProviderOperation.Builder builder = referenceRawContact(
|
||||
ContentProviderOperation.newInsert(Data.CONTENT_URI), rawContactId);
|
||||
|
||||
if (userId.email().startsWith("xmpp:")) {
|
||||
builder = builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE)
|
||||
.withValue(Im.PROTOCOL, Im.PROTOCOL_JABBER)
|
||||
.withValue(Im.DATA, userId.email().substring(5));
|
||||
} else {
|
||||
builder = builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
|
||||
.withValue(Email.DATA, userId.email());
|
||||
}
|
||||
ops.add(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeContactDisplayName(ArrayList<ContentProviderOperation> ops, long rawContactId,
|
||||
String displayName) {
|
||||
if (displayName != null) {
|
||||
ops.add(insertOrUpdateForRawContact(ContactsContract.Data.CONTENT_URI, rawContactId,
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private ContentProviderOperation.Builder referenceRawContact(ContentProviderOperation.Builder builder,
|
||||
long rawContactId) {
|
||||
return rawContactId == -1 ?
|
||||
builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) :
|
||||
builder.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
|
||||
}
|
||||
|
||||
private ContentProviderOperation.Builder insertOrUpdateForRawContact(Uri uri, long rawContactId,
|
||||
String itemType) {
|
||||
if (rawContactId == -1) {
|
||||
return referenceRawContact(ContentProviderOperation.newInsert(uri), rawContactId).withValue(
|
||||
ContactsContract.Data.MIMETYPE, itemType);
|
||||
} else {
|
||||
return selectByRawContactAndItemType(ContentProviderOperation.newUpdate(uri), rawContactId, itemType);
|
||||
}
|
||||
}
|
||||
|
||||
private ContentProviderOperation.Builder selectByRawContactAndItemType(
|
||||
ContentProviderOperation.Builder builder, long rawContactId, String itemType) {
|
||||
return builder.withSelection(
|
||||
ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?",
|
||||
new String[]{
|
||||
Long.toString(rawContactId), itemType
|
||||
});
|
||||
}
|
||||
}
|
|
@ -359,30 +359,6 @@ public class Preferences {
|
|||
mSharedPreferences.edit().putString(Pref.SYNC_WORK_UUID, value).apply();
|
||||
}
|
||||
|
||||
public boolean isAnalyticsAskedPolitely() {
|
||||
return mSharedPreferences.getBoolean(Pref.KEY_ANALYTICS_ASKED_POLITELY, false);
|
||||
}
|
||||
|
||||
public void setAnalyticsAskedPolitely() {
|
||||
mSharedPreferences.edit().putBoolean(Pref.KEY_ANALYTICS_ASKED_POLITELY, true).apply();
|
||||
}
|
||||
|
||||
public boolean isAnalyticsHasConsent() {
|
||||
return mSharedPreferences.getBoolean(Pref.KEY_ANALYTICS_CONSENT, false);
|
||||
}
|
||||
|
||||
public void setAnalyticsGotUserConsent(boolean hasUserConsent) {
|
||||
mSharedPreferences.edit().putBoolean(Pref.KEY_ANALYTICS_CONSENT, hasUserConsent).apply();
|
||||
}
|
||||
|
||||
public void setAnalyticsLastAskedNow() {
|
||||
mSharedPreferences.edit().putLong(Pref.KEY_ANALYTICS_LAST_ASKED, System.currentTimeMillis()).apply();
|
||||
}
|
||||
|
||||
public long getAnalyticsLastAsked() {
|
||||
return mSharedPreferences.getLong(Pref.KEY_ANALYTICS_LAST_ASKED, 0);
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public static abstract class CloudSearchPrefs implements Parcelable {
|
||||
public abstract boolean isKeyserverEnabled();
|
||||
|
|
|
@ -14,13 +14,14 @@
|
|||
android:text="@string/create_key_add_email_text"
|
||||
android:textAppearance="?android:textAppearanceMedium" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.EmailEditText
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/add_email_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="@string/label_email"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textEmailAddress"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -8,15 +8,16 @@
|
|||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp">
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.EmailEditText
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/add_user_id_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/label_email"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textEmailAddress"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.NameEditText
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/add_user_id_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -25,13 +25,14 @@
|
|||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/create_key_email_text" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.EmailEditText
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/create_key_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textEmailAddress"
|
||||
android:hint="@string/label_email"
|
||||
android:ems="10" />
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/create_key_name_text" />
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.NameEditText
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/create_key_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/shop_webView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/shop_progressbar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/shop_webView"
|
||||
app:layout_constraintEnd_toEndOf="@+id/shop_webView"
|
||||
app:layout_constraintStart_toStartOf="@+id/shop_webView"
|
||||
app:layout_constraintTop_toTopOf="@+id/shop_webView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/shop_progressbar_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/shop_loading_label"
|
||||
app:layout_constraintEnd_toEndOf="@+id/shop_progressbar"
|
||||
app:layout_constraintStart_toStartOf="@+id/shop_progressbar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/shop_progressbar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -34,37 +34,6 @@
|
|||
android:fitsSystemWindows="true"
|
||||
app:layout_collapseMode="parallax">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/view_key_photo_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="false"
|
||||
android:fitsSystemWindows="true"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/view_key_photo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:adjustViewBounds="false"
|
||||
android:baselineAlignBottom="false"
|
||||
android:cropToPadding="false"
|
||||
android:fitsSystemWindows="true"
|
||||
android:focusable="false"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
<!-- text protection scrim -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="@drawable/scrim_bottom" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/view_key_status"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
|
@ -63,16 +62,4 @@
|
|||
card_view:cardUseCompatPadding="true"
|
||||
/>
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView
|
||||
android:id="@+id/linked_system_contact_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
card_view:cardBackgroundColor="?attr/colorCardViewBackground"
|
||||
card_view:cardCornerRadius="4dp"
|
||||
card_view:cardElevation="2dp"
|
||||
card_view:cardUseCompatPadding="true"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
able
|
||||
angry
|
||||
bad
|
||||
bent
|
||||
bitter
|
||||
black
|
||||
blue
|
||||
boiling
|
||||
bright
|
||||
broken
|
||||
brown
|
||||
certain
|
||||
cheap
|
||||
clean
|
||||
clear
|
||||
cold
|
||||
common
|
||||
complex
|
||||
cruel
|
||||
dark
|
||||
dead
|
||||
dear
|
||||
deep
|
||||
dirty
|
||||
dry
|
||||
early
|
||||
elastic
|
||||
equal
|
||||
false
|
||||
fat
|
||||
feeble
|
||||
female
|
||||
fertile
|
||||
first
|
||||
fixed
|
||||
flat
|
||||
foolish
|
||||
free
|
||||
full
|
||||
future
|
||||
general
|
||||
good
|
||||
great
|
||||
green
|
||||
grey
|
||||
hanging
|
||||
happy
|
||||
hard
|
||||
healthy
|
||||
high
|
||||
hollow
|
||||
kind
|
||||
last
|
||||
late
|
||||
lazy
|
||||
left
|
||||
like
|
||||
living
|
||||
long
|
||||
loose
|
||||
loud
|
||||
low
|
||||
male
|
||||
married
|
||||
medical
|
||||
mixed
|
||||
narrow
|
||||
natural
|
||||
new
|
||||
normal
|
||||
old
|
||||
open
|
||||
past
|
||||
poor
|
||||
present
|
||||
pretty
|
||||
private
|
||||
public
|
||||
quick
|
||||
quiet
|
||||
ready
|
||||
rare
|
||||
red
|
||||
regular
|
||||
right
|
||||
rough
|
||||
round
|
||||
sad
|
||||
safe
|
||||
same
|
||||
second
|
||||
secret
|
||||
serious
|
||||
sharp
|
||||
short
|
||||
shut
|
||||
sick
|
||||
simple
|
||||
slow
|
||||
small
|
||||
smooth
|
||||
soft
|
||||
solid
|
||||
sour
|
||||
special
|
||||
sticky
|
||||
stiff
|
||||
strange
|
||||
strong
|
||||
sudden
|
||||
sweet
|
||||
tall
|
||||
thick
|
||||
thin
|
||||
tight
|
||||
tired
|
||||
true
|
||||
unknown
|
||||
violent
|
||||
waiting
|
||||
warm
|
||||
wet
|
||||
white
|
||||
wide
|
||||
wise
|
||||
wrong
|
||||
yellow
|
||||
young
|
|
@ -1,64 +0,0 @@
|
|||
ably
|
||||
angrily
|
||||
badly
|
||||
bitterly
|
||||
brightly
|
||||
brokenly
|
||||
cheaply
|
||||
clearly
|
||||
coldly
|
||||
commonly
|
||||
cruelly
|
||||
darkly
|
||||
dearly
|
||||
deeply
|
||||
drily
|
||||
equally
|
||||
falsely
|
||||
feebly
|
||||
fixedly
|
||||
flatly
|
||||
freely
|
||||
fully
|
||||
greatly
|
||||
happily
|
||||
hardly
|
||||
kindly
|
||||
lately
|
||||
lazily
|
||||
loosely
|
||||
loudly
|
||||
narrowly
|
||||
newly
|
||||
normally
|
||||
openly
|
||||
poorly
|
||||
prettily
|
||||
publicly
|
||||
quickly
|
||||
quietly
|
||||
readily
|
||||
rarely
|
||||
roughly
|
||||
sadly
|
||||
safely
|
||||
secretly
|
||||
sharply
|
||||
simply
|
||||
slowly
|
||||
smoothly
|
||||
softly
|
||||
solidly
|
||||
sourly
|
||||
stiffly
|
||||
strongly
|
||||
suddenly
|
||||
sweetly
|
||||
thickly
|
||||
thinly
|
||||
tightly
|
||||
tiredly
|
||||
truly
|
||||
warmly
|
||||
widely
|
||||
wisely
|
|
@ -1,8 +0,0 @@
|
|||
her
|
||||
his
|
||||
my
|
||||
our
|
||||
that
|
||||
the
|
||||
this
|
||||
your
|
|
@ -1,512 +0,0 @@
|
|||
account
|
||||
air
|
||||
amount
|
||||
angle
|
||||
animal
|
||||
answer
|
||||
ant
|
||||
apple
|
||||
arch
|
||||
arm
|
||||
army
|
||||
attack
|
||||
attempt
|
||||
baby
|
||||
back
|
||||
bag
|
||||
ball
|
||||
band
|
||||
base
|
||||
basin
|
||||
basket
|
||||
bath
|
||||
bed
|
||||
bee
|
||||
belief
|
||||
bell
|
||||
berry
|
||||
bird
|
||||
birth
|
||||
bite
|
||||
blade
|
||||
blood
|
||||
blow
|
||||
board
|
||||
boat
|
||||
body
|
||||
bone
|
||||
book
|
||||
boot
|
||||
bottle
|
||||
box
|
||||
boy
|
||||
brain
|
||||
brake
|
||||
branch
|
||||
bread
|
||||
breath
|
||||
brick
|
||||
bridge
|
||||
brother
|
||||
brush
|
||||
bucket
|
||||
bulb
|
||||
burn
|
||||
butter
|
||||
button
|
||||
cake
|
||||
camera
|
||||
canvas
|
||||
car
|
||||
card
|
||||
cat
|
||||
cause
|
||||
chain
|
||||
chalk
|
||||
chance
|
||||
change
|
||||
cheese
|
||||
chest
|
||||
chin
|
||||
church
|
||||
circle
|
||||
clock
|
||||
cloth
|
||||
cloud
|
||||
coal
|
||||
coat
|
||||
collar
|
||||
colour
|
||||
comb
|
||||
comfort
|
||||
company
|
||||
control
|
||||
cook
|
||||
copper
|
||||
copy
|
||||
cord
|
||||
cork
|
||||
cotton
|
||||
cough
|
||||
country
|
||||
cover
|
||||
cow
|
||||
crack
|
||||
credit
|
||||
crime
|
||||
crush
|
||||
cry
|
||||
cup
|
||||
current
|
||||
curtain
|
||||
curve
|
||||
cushion
|
||||
damage
|
||||
danger
|
||||
day
|
||||
debt
|
||||
degree
|
||||
design
|
||||
desire
|
||||
detail
|
||||
disease
|
||||
disgust
|
||||
dog
|
||||
door
|
||||
doubt
|
||||
drain
|
||||
drawer
|
||||
dress
|
||||
drink
|
||||
drop
|
||||
dust
|
||||
ear
|
||||
earth
|
||||
edge
|
||||
effect
|
||||
egg
|
||||
end
|
||||
engine
|
||||
error
|
||||
event
|
||||
example
|
||||
expert
|
||||
eye
|
||||
face
|
||||
fact
|
||||
fall
|
||||
family
|
||||
farm
|
||||
father
|
||||
fear
|
||||
feather
|
||||
feeling
|
||||
fiction
|
||||
field
|
||||
fight
|
||||
finger
|
||||
fire
|
||||
fish
|
||||
flag
|
||||
flame
|
||||
flight
|
||||
floor
|
||||
flower
|
||||
fly
|
||||
fold
|
||||
food
|
||||
foot
|
||||
force
|
||||
fork
|
||||
form
|
||||
fowl
|
||||
frame
|
||||
friend
|
||||
front
|
||||
fruit
|
||||
garden
|
||||
girl
|
||||
glass
|
||||
glove
|
||||
goat
|
||||
gold
|
||||
grain
|
||||
grass
|
||||
grip
|
||||
group
|
||||
growth
|
||||
guide
|
||||
gun
|
||||
hair
|
||||
hammer
|
||||
hand
|
||||
harbour
|
||||
harmony
|
||||
hat
|
||||
hate
|
||||
head
|
||||
heart
|
||||
heat
|
||||
history
|
||||
hole
|
||||
hook
|
||||
hope
|
||||
horn
|
||||
horse
|
||||
hour
|
||||
house
|
||||
humour
|
||||
ice
|
||||
idea
|
||||
impulse
|
||||
ink
|
||||
insect
|
||||
iron
|
||||
island
|
||||
jelly
|
||||
jewel
|
||||
join
|
||||
journey
|
||||
judge
|
||||
jump
|
||||
kettle
|
||||
key
|
||||
kick
|
||||
kiss
|
||||
knee
|
||||
knife
|
||||
knot
|
||||
land
|
||||
laugh
|
||||
law
|
||||
lead
|
||||
leaf
|
||||
leather
|
||||
leg
|
||||
letter
|
||||
level
|
||||
library
|
||||
lift
|
||||
light
|
||||
limit
|
||||
line
|
||||
linen
|
||||
lip
|
||||
liquid
|
||||
list
|
||||
lock
|
||||
look
|
||||
loss
|
||||
love
|
||||
machine
|
||||
man
|
||||
manager
|
||||
map
|
||||
mark
|
||||
market
|
||||
mass
|
||||
match
|
||||
meal
|
||||
measure
|
||||
meat
|
||||
meeting
|
||||
memory
|
||||
metal
|
||||
middle
|
||||
milk
|
||||
mind
|
||||
mine
|
||||
minute
|
||||
mist
|
||||
money
|
||||
monkey
|
||||
month
|
||||
moon
|
||||
morning
|
||||
mother
|
||||
motion
|
||||
mouth
|
||||
move
|
||||
muscle
|
||||
music
|
||||
nail
|
||||
name
|
||||
nation
|
||||
neck
|
||||
need
|
||||
needle
|
||||
nerve
|
||||
net
|
||||
news
|
||||
night
|
||||
noise
|
||||
nose
|
||||
note
|
||||
number
|
||||
nut
|
||||
offer
|
||||
office
|
||||
oil
|
||||
opinion
|
||||
orange
|
||||
order
|
||||
oven
|
||||
owner
|
||||
page
|
||||
pain
|
||||
paint
|
||||
paper
|
||||
parcel
|
||||
part
|
||||
paste
|
||||
payment
|
||||
peace
|
||||
pen
|
||||
pencil
|
||||
person
|
||||
picture
|
||||
pig
|
||||
pin
|
||||
pipe
|
||||
place
|
||||
plane
|
||||
plant
|
||||
plate
|
||||
play
|
||||
plough
|
||||
pocket
|
||||
point
|
||||
poison
|
||||
polish
|
||||
porter
|
||||
pot
|
||||
potato
|
||||
powder
|
||||
power
|
||||
price
|
||||
print
|
||||
prison
|
||||
process
|
||||
produce
|
||||
profit
|
||||
prose
|
||||
protest
|
||||
pull
|
||||
pump
|
||||
purpose
|
||||
push
|
||||
quality
|
||||
rail
|
||||
rain
|
||||
range
|
||||
rat
|
||||
rate
|
||||
ray
|
||||
reason
|
||||
receipt
|
||||
record
|
||||
regret
|
||||
request
|
||||
respect
|
||||
rest
|
||||
reward
|
||||
rhythm
|
||||
rice
|
||||
ring
|
||||
river
|
||||
road
|
||||
rod
|
||||
roll
|
||||
roof
|
||||
room
|
||||
root
|
||||
rub
|
||||
rule
|
||||
run
|
||||
sail
|
||||
salt
|
||||
sand
|
||||
scale
|
||||
school
|
||||
science
|
||||
screw
|
||||
sea
|
||||
seat
|
||||
seed
|
||||
self
|
||||
sense
|
||||
servant
|
||||
sex
|
||||
shade
|
||||
shake
|
||||
shame
|
||||
sheep
|
||||
shelf
|
||||
ship
|
||||
shirt
|
||||
shock
|
||||
shoe
|
||||
side
|
||||
sign
|
||||
silk
|
||||
silver
|
||||
sister
|
||||
size
|
||||
skin
|
||||
skirt
|
||||
sky
|
||||
sleep
|
||||
slip
|
||||
slope
|
||||
smash
|
||||
smell
|
||||
smile
|
||||
smoke
|
||||
snake
|
||||
sneeze
|
||||
snow
|
||||
soap
|
||||
society
|
||||
sock
|
||||
son
|
||||
song
|
||||
sort
|
||||
sound
|
||||
soup
|
||||
space
|
||||
spade
|
||||
sponge
|
||||
spoon
|
||||
spring
|
||||
square
|
||||
stage
|
||||
stamp
|
||||
star
|
||||
start
|
||||
station
|
||||
steam
|
||||
steel
|
||||
stem
|
||||
step
|
||||
stick
|
||||
stitch
|
||||
stomach
|
||||
stone
|
||||
stop
|
||||
store
|
||||
story
|
||||
street
|
||||
stretch
|
||||
sugar
|
||||
summer
|
||||
sun
|
||||
support
|
||||
swim
|
||||
system
|
||||
table
|
||||
tail
|
||||
talk
|
||||
taste
|
||||
tax
|
||||
test
|
||||
theory
|
||||
thing
|
||||
thought
|
||||
thread
|
||||
throat
|
||||
thumb
|
||||
thunder
|
||||
ticket
|
||||
time
|
||||
tin
|
||||
toe
|
||||
tongue
|
||||
tooth
|
||||
top
|
||||
touch
|
||||
town
|
||||
trade
|
||||
train
|
||||
tray
|
||||
tree
|
||||
trick
|
||||
trouble
|
||||
turn
|
||||
twist
|
||||
unit
|
||||
value
|
||||
verse
|
||||
vessel
|
||||
view
|
||||
voice
|
||||
walk
|
||||
wall
|
||||
war
|
||||
wash
|
||||
waste
|
||||
watch
|
||||
water
|
||||
wave
|
||||
wax
|
||||
way
|
||||
weather
|
||||
week
|
||||
weight
|
||||
wheel
|
||||
whip
|
||||
whistle
|
||||
wind
|
||||
window
|
||||
wine
|
||||
wing
|
||||
winter
|
||||
wire
|
||||
woman
|
||||
wood
|
||||
wool
|
||||
word
|
||||
work
|
||||
worm
|
||||
wound
|
||||
writing
|
||||
year
|
|
@ -1,32 +0,0 @@
|
|||
above
|
||||
across
|
||||
after
|
||||
against
|
||||
along
|
||||
among
|
||||
around
|
||||
at
|
||||
before
|
||||
behind
|
||||
below
|
||||
beneath
|
||||
beside
|
||||
between
|
||||
beyond
|
||||
by
|
||||
from
|
||||
in
|
||||
inside
|
||||
into
|
||||
near
|
||||
on
|
||||
outside
|
||||
over
|
||||
past
|
||||
round
|
||||
through
|
||||
to
|
||||
towards
|
||||
under
|
||||
upon
|
||||
with
|
|
@ -1,128 +0,0 @@
|
|||
agrees
|
||||
allows
|
||||
answers
|
||||
arrives
|
||||
asks
|
||||
is
|
||||
becomes
|
||||
begins
|
||||
believes
|
||||
brings
|
||||
burns
|
||||
buys
|
||||
calls
|
||||
changes
|
||||
chooses
|
||||
cleans
|
||||
closes
|
||||
comes
|
||||
compares
|
||||
continues
|
||||
cooks
|
||||
costs
|
||||
counts
|
||||
cries
|
||||
cuts
|
||||
dances
|
||||
decides
|
||||
describes
|
||||
destroys
|
||||
dies
|
||||
does
|
||||
drinks
|
||||
drives
|
||||
eats
|
||||
ends
|
||||
explains
|
||||
falls
|
||||
feels
|
||||
fights
|
||||
fills
|
||||
finds
|
||||
finishes
|
||||
forgets
|
||||
forgives
|
||||
gets
|
||||
gives
|
||||
goes
|
||||
grows
|
||||
hates
|
||||
has
|
||||
hears
|
||||
helps
|
||||
hides
|
||||
holds
|
||||
hurts
|
||||
improves
|
||||
jumps
|
||||
keeps
|
||||
kills
|
||||
knows
|
||||
laughs
|
||||
learns
|
||||
leaves
|
||||
lets
|
||||
lies
|
||||
listens
|
||||
lives
|
||||
looks
|
||||
loses
|
||||
loves
|
||||
makes
|
||||
meets
|
||||
moves
|
||||
needs
|
||||
occurs
|
||||
offers
|
||||
opens
|
||||
pays
|
||||
plays
|
||||
prefers
|
||||
prepares
|
||||
presses
|
||||
promises
|
||||
pulls
|
||||
pushes
|
||||
puts
|
||||
reads
|
||||
receives
|
||||
remembers
|
||||
repeats
|
||||
rests
|
||||
returns
|
||||
runs
|
||||
sees
|
||||
sells
|
||||
sends
|
||||
shouts
|
||||
shows
|
||||
sings
|
||||
sits
|
||||
sleeps
|
||||
smiles
|
||||
speaks
|
||||
starts
|
||||
stays
|
||||
stops
|
||||
studies
|
||||
suggests
|
||||
supports
|
||||
takes
|
||||
talks
|
||||
teaches
|
||||
tells
|
||||
thinks
|
||||
throws
|
||||
touches
|
||||
travels
|
||||
treats
|
||||
tries
|
||||
turns
|
||||
uses
|
||||
visits
|
||||
walks
|
||||
wants
|
||||
washes
|
||||
wins
|
||||
works
|
||||
writes
|
|
@ -1,128 +0,0 @@
|
|||
agrees with
|
||||
allows
|
||||
answers
|
||||
arrives at
|
||||
asks
|
||||
is
|
||||
becomes
|
||||
begins
|
||||
believes
|
||||
brings
|
||||
burns
|
||||
buys
|
||||
calls
|
||||
changes
|
||||
chooses
|
||||
cleans
|
||||
closes
|
||||
comes to
|
||||
compares
|
||||
continues
|
||||
cooks
|
||||
costs
|
||||
counts
|
||||
cries for
|
||||
cuts
|
||||
dances with
|
||||
decides on
|
||||
describes
|
||||
destroys
|
||||
dies for
|
||||
does
|
||||
drinks
|
||||
drives
|
||||
eats
|
||||
ends
|
||||
explains
|
||||
falls on
|
||||
feels
|
||||
fights
|
||||
fills
|
||||
finds
|
||||
finishes
|
||||
forgets
|
||||
forgives
|
||||
gets
|
||||
gives
|
||||
goes to
|
||||
grows
|
||||
hates
|
||||
has
|
||||
hears
|
||||
helps
|
||||
hides
|
||||
holds
|
||||
hurts
|
||||
improves
|
||||
jumps over
|
||||
keeps
|
||||
kills
|
||||
knows
|
||||
laughs at
|
||||
learns
|
||||
leaves
|
||||
lets
|
||||
lies to
|
||||
listens to
|
||||
lives with
|
||||
looks at
|
||||
loses
|
||||
loves
|
||||
makes
|
||||
meets
|
||||
moves
|
||||
needs
|
||||
occurs to
|
||||
offers
|
||||
opens
|
||||
pays
|
||||
plays
|
||||
prefers
|
||||
prepares
|
||||
presses
|
||||
promises
|
||||
pulls
|
||||
pushes
|
||||
puts
|
||||
reads
|
||||
receives
|
||||
remembers
|
||||
repeats
|
||||
rests by
|
||||
returns
|
||||
runs to
|
||||
sees
|
||||
sells
|
||||
sends
|
||||
shouts at
|
||||
shows
|
||||
sings to
|
||||
sits by
|
||||
sleeps by
|
||||
smiles at
|
||||
speaks to
|
||||
starts
|
||||
stays with
|
||||
stops
|
||||
studies
|
||||
suggests
|
||||
supports
|
||||
takes
|
||||
talks to
|
||||
teaches
|
||||
tells
|
||||
thinks of
|
||||
throws
|
||||
touches
|
||||
travels to
|
||||
treats
|
||||
tries
|
||||
turns
|
||||
uses
|
||||
visits
|
||||
walks to
|
||||
wants
|
||||
washes
|
||||
wins
|
||||
works for
|
||||
writes to
|
|
@ -178,8 +178,7 @@
|
|||
<string name="label_sync_settings_contacts_title">ربط المفاتيح بجهات الإتصال</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">لن يتم ربط المفاتيح الجديدة بجهات الإتصال</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">تحديثات تلقائية للمفاتيح</string>
|
||||
<string name="label_experimental_settings_desc_title">تحذير</string>
|
||||
<string name="label_experimental_settings_desc_title">تحذير</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">فعّل تور</string>
|
||||
<string name="pref_proxy_tor_summary">يتطلب نتصيب تطبيق أوربوت</string>
|
||||
|
@ -686,7 +685,5 @@
|
|||
<string name="notify_content_keysync">مفتاح %d / %d</string>
|
||||
<string name="keylist_header_anonymous">مجهول</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="button_analytics_no">لا، شكراً</string>
|
||||
<string name="snackbutton_analytics_settings">الإعدادات</string>
|
||||
<string name="share_key">شارك المفتاح</string>
|
||||
</resources>
|
||||
|
|
|
@ -167,6 +167,4 @@
|
|||
<string name="transfer_confirm_ok">Kas</string>
|
||||
<string name="token_action_import">Emporzhiañ</string>
|
||||
<string name="keylist_header_anonymous">Anonymous</string>
|
||||
<string name="button_analytics_no">Nann, avat</string>
|
||||
<string name="snackbutton_analytics_settings">Arventennoù</string>
|
||||
</resources>
|
||||
|
|
|
@ -208,13 +208,10 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Enllaça claus amb contactes basant-se amb noms i adreces de correu. Això succeeix completament fora de línia en el vostre dispositiu.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Les noves claus no s\'enllaçaran amb els contactes</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Actualització de claus automàticament</string>
|
||||
<string name="label_experimental_settings_desc_title">Advertència</string>
|
||||
<string name="label_experimental_settings_keybase_title">Proves de Keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Contacta amb keybase.io per proves de clau i mostra-les cada vegada que es mostri una clau</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Les icones i moltes pantalles no estan encara ben ajustades per aquest tema fosc)</string>
|
||||
<string name="label_settings_analytics_title">Permet estadístiques d\'ús anònim</string>
|
||||
<string name="label_settings_analytics_summary">Si està habilitat, envia estadístiques d\'ús anònim per ajudar en la millora de la app</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Activa Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Requereix Orbot per a ser instal·lat</string>
|
||||
|
@ -700,8 +697,6 @@
|
|||
<string name="my_keys">Les meves claus</string>
|
||||
<string name="nav_backup">Còpia de seguretat/Restaura</string>
|
||||
<string name="nav_transfer">Transferència Wifi segura</string>
|
||||
<string name="nav_shop">Botiga</string>
|
||||
<string name="nav_autocrypt">Proveu Autocrypt amb Thunderbird</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Escriviu text</string>
|
||||
<!--certs-->
|
||||
|
@ -822,6 +817,4 @@
|
|||
<string name="snack_keylist_clipboard_action">Visualitza</string>
|
||||
<string name="notify_title_keysync">Actualitzant claus...</string>
|
||||
<string name="keylist_header_anonymous">Anònim </string>
|
||||
<string name="button_analytics_no">No, gràcies</string>
|
||||
<string name="snackbutton_analytics_settings">Configuració</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Protojit klíče s kontakty na základě jmen a emailových adres. Odehrává se kompletně offline na vašem zařízení.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nové klíče nebudou propojeny s kontakty</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automaticky aktualizovat klíč</string>
|
||||
<string name="label_experimental_settings_desc_title">Varovnání</string>
|
||||
<string name="label_experimental_settings_desc_summary">Tyto funkce nejsou ještě dokončené nebo neprošli uživatelským testováním/bezpečnostním auditem. Prosím, nespoléhejte na jejich zabezpečení a nehlašte problémy s nimi spojené, pokud na ně narazíte.</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io Proofs</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Kontaktovat keybase.io pro proofs klíče a ukázat je pokaždé když je klíč zobrazen</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Ikony a mnoho obrazovek ještě nejsou uzpůsobené pro temné téma vzhledu)</string>
|
||||
<string name="label_settings_analytics_title">Povolit anonymní statistiku využití</string>
|
||||
<string name="label_settings_analytics_summary">Pokud povoleno, odešle anonymní statistiku o využívání za účelem vylepšení applikace</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Zapnout Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Vyžaduje instalovaný Orbot </string>
|
||||
|
@ -816,8 +813,6 @@
|
|||
<string name="my_keys">Moje Klíče</string>
|
||||
<string name="nav_backup">Záloha/Obnova</string>
|
||||
<string name="nav_transfer">Bezpečný přenos přes WiFi</string>
|
||||
<string name="nav_shop">Obchod</string>
|
||||
<string name="nav_autocrypt">Vyzkoušejte Autocrypt s Thunderbird</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Napsat text</string>
|
||||
<!--certs-->
|
||||
|
@ -1874,12 +1869,6 @@
|
|||
<string name="keylist_item_key_id">Klíč ID: %s</string>
|
||||
<string name="keylist_header_anonymous">Anonymní</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">K zlepšení zkušenosti všech uživatelů, smí OpenKeychain sbírat anonymní statistiku o používání?\n\nVíce informací v <a href="https://openkeychain.org/help/privacy-policy">Ochraně údajů</a>.</string>
|
||||
<string name="button_analytics_yes">Ano, chci pomoci!</string>
|
||||
<string name="button_analytics_no">Ne, děkuji</string>
|
||||
<string name="snack_analytics_accept">Díky za pomoc! Tuto volbu můžete změnit v nastavení.</string>
|
||||
<string name="snack_analytics_reject">To je v pořádku, už se nezeptáme. Své rozhodnutí mužete změnit v nastavení.</string>
|
||||
<string name="snackbutton_analytics_settings">Nastavení</string>
|
||||
<string name="subkey_action_create">Založím podklíč</string>
|
||||
<string name="subkey_action_revoke">Podklíč bude zrušen</string>
|
||||
<string name="subkey_action_strip">Podklíč bude vyjmut</string>
|
||||
|
@ -1887,6 +1876,4 @@
|
|||
<string name="subkey_action_expiry_date">Doba platnosti se změni do %s</string>
|
||||
<string name="share_key_clipboard">Sdílet klíč skrze schránku</string>
|
||||
<string name="share_key">Sdílet klíč</string>
|
||||
<string name="shop_loading_label">Otevírám obchod...</string>
|
||||
<string name="shop_title">COTECH Security Keys</string>
|
||||
</resources>
|
||||
|
|
|
@ -227,5 +227,4 @@
|
|||
<string name="key_gen_back">Tilbage</string>
|
||||
<string name="key_gen_finish">Afslut</string>
|
||||
<string name="snack_keylist_clipboard_action">Gennemse</string>
|
||||
<string name="snackbutton_analytics_settings">Indstillinger</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Schlüssel basierend auf Namen und E-Mail-Adressen mit Kontakten verknüpfen. Das alles findet komplett offline auf deinem Gerät statt.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Neue Schlüssel werden nicht mit Kontakten verknüpft</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatische Schlüsselaktualisierung</string>
|
||||
<string name="label_experimental_settings_desc_title">Warnung</string>
|
||||
<string name="label_experimental_settings_desc_summary">Diese Funktionen sind noch nicht final oder das Ergebnis von Benutzererfahrungs-/Sicherheitsuntersuchungen. Verlasse dich daher nicht auf deren Sicherheit und melde uns bitte keine auftretenden Probleme!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io-Nachweise</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Keybase.io für Schlüsselnachweise kontaktieren und diese jedesmal zeigen, wenn ein Schlüssel angezeigt wird</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Die Symbole und viele Bildschirme sind noch nicht an das dunkle Design angepasst)</string>
|
||||
<string name="label_settings_analytics_title">Erlaube anonyme Benutzungsstatistiken</string>
|
||||
<string name="label_settings_analytics_summary">Wenn aktiviert, sendet anonyme Benutzungsstatistiken, um dabei zu helfen, die App zu verbessern</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Tor aktivieren</string>
|
||||
<string name="pref_proxy_tor_summary">Orbot muss installiert sein</string>
|
||||
|
@ -780,8 +777,6 @@
|
|||
<string name="my_keys">Meine Schlüssel</string>
|
||||
<string name="nav_backup">Backup/Wiederherstellung</string>
|
||||
<string name="nav_transfer">Sichere WiFi Übertragung</string>
|
||||
<string name="nav_shop">Shop</string>
|
||||
<string name="nav_autocrypt">Autocrypt mit Thunderbird probieren</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Text eingeben</string>
|
||||
<!--certs-->
|
||||
|
@ -1807,12 +1802,6 @@ Ein sicherer Schlüssel sollte eine Stärke von 2048 Bits besitzen.</string>
|
|||
<string name="keylist_item_key_id">Key ID: %s</string>
|
||||
<string name="keylist_header_anonymous">Anonym</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Um die Erfahrung aller Benutzer zu verbessen, soll OpenKeychain anonyme Benutzungsstatistiken sammeln dürfen?\n\nUm mehr zu erfahren, schau in unsere<a href="https://openkeychain.org/help/privacy-policy">Privatsphärenrichtlinien</a>.</string>
|
||||
<string name="button_analytics_yes">Ja, ich möchte helfen!</string>
|
||||
<string name="button_analytics_no">Nein, danke</string>
|
||||
<string name="snack_analytics_accept">Vielen Dank für die Hilfe! Du kannst deine Entscheidung in den Einstellungen ändern.</string>
|
||||
<string name="snack_analytics_reject">Das ist in Ordnung, wir werden nicht erneut fragen. Du kannst deine Entscheidung in den Einstellungen ändern.</string>
|
||||
<string name="snackbutton_analytics_settings">Einstellungen</string>
|
||||
<string name="subkey_action_create">Teilschlüssel wird erstellt werden</string>
|
||||
<string name="subkey_action_revoke">Teilschlüssel wird widerrufen werden</string>
|
||||
<string name="subkey_action_strip">Teilschlüssel wird entfernt werden</string>
|
||||
|
@ -1820,6 +1809,4 @@ Ein sicherer Schlüssel sollte eine Stärke von 2048 Bits besitzen.</string>
|
|||
<string name="subkey_action_expiry_date">Ablauf wird geändert zu \"%s\"</string>
|
||||
<string name="share_key_clipboard">Schlüssel per Zwischenablage teilen</string>
|
||||
<string name="share_key">Schlüssel teilen</string>
|
||||
<string name="shop_loading_label">Shop wird geladen...</string>
|
||||
<string name="shop_title">COTECH Sicherheitsschlüssel</string>
|
||||
</resources>
|
||||
|
|
|
@ -299,6 +299,4 @@
|
|||
<string name="snack_keylist_clipboard_action">Δείτε τα αποτελέσματα</string>
|
||||
<string name="keylist_header_anonymous">Ανώνυμος</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="button_analytics_no">Όχι, ευχαριστώ</string>
|
||||
<string name="snackbutton_analytics_settings">Ρυθμίσεις</string>
|
||||
</resources>
|
||||
|
|
|
@ -176,5 +176,4 @@
|
|||
<string name="key_gen_back">Reen</string>
|
||||
<string name="key_gen_finish">Fini</string>
|
||||
<string name="snack_keylist_clipboard_action">Rigardi</string>
|
||||
<string name="snackbutton_analytics_settings">Agordoj</string>
|
||||
</resources>
|
||||
|
|
|
@ -210,8 +210,7 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Vincular claves a contactos basándose en nombres y direcciones de correo electrónico. Esto ocurre sin conexión completamente en tu dispositivo.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nuevas claves no serán vinculadas a contactos</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Actualizaciones automáticas de claves</string>
|
||||
<string name="label_experimental_settings_desc_title">Advertencia</string>
|
||||
<string name="label_experimental_settings_desc_title">Advertencia</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Activar Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Requiere Orbot instalado</string>
|
||||
|
@ -434,5 +433,4 @@
|
|||
<string name="select_identity_cancel">Desactivar</string>
|
||||
<string name="key_gen_back">Atrás</string>
|
||||
<string name="snack_keylist_clipboard_action">Ve</string>
|
||||
<string name="snackbutton_analytics_settings">Ajustes</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Vincula claves con contactos basándose en nombres y direcciones de correo electrónico. Esto sucede con su dispositivo completamente desconectado.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Las claves nuevas no se vincularán a contactos</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Actualizaciones automáticas de claves</string>
|
||||
<string name="label_experimental_settings_desc_title">Advertencia</string>
|
||||
<string name="label_experimental_settings_desc_title">Advertencia</string>
|
||||
<string name="label_experimental_settings_desc_summary">Estas características todavía no están finalizadas o no se han obtenido los resultados de experiencia del usuario y la investigación de seguridad. Por tanto, no confíe en su seguridad y, por favor, ¡no informe de los problemas que encuentre!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Comprobantes de Keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Contacta con keybase.io para obtener comprobantes de clave y muestrelos cada vez que se muestra una clave</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Los iconos y algunas pantallas todavía no están ajustadas de acuerdo con el tema decorativo oscuro)</string>
|
||||
<string name="label_settings_analytics_title">Permitir estadísticas de uso anónimas</string>
|
||||
<string name="label_settings_analytics_summary">Si está habilitado, envía estadísticas de uso anónimas para ayudar a mejorar la app</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Habilitar Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Requiere que Orbot esté instalado</string>
|
||||
|
@ -1804,12 +1801,6 @@
|
|||
<string name="keylist_item_key_id">Identificación de la clave: %s</string>
|
||||
<string name="keylist_header_anonymous">Anónimo</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Para mejorar la experiencia de todos los usuarios, ¿puede OpenKeychain recopilar estadísticas de uso anónimas? \n\n Para saber más, mira nuestra <a href="https://openkeychain.org/help/privacy-policy">Política de Privacidad</a>.</string>
|
||||
<string name="button_analytics_yes">Si, quiero ayudar!</string>
|
||||
<string name="button_analytics_no">No, gracias!</string>
|
||||
<string name="snack_analytics_accept">Gracias por ayudar! Puede cambiar esta preferencia en la configuración.</string>
|
||||
<string name="snack_analytics_reject">Está bien, no volveremos a preguntar. Puedes cambiar de opinión en la configuración.</string>
|
||||
<string name="snackbutton_analytics_settings">Configuración</string>
|
||||
<string name="subkey_action_create">La subclave será creada</string>
|
||||
<string name="subkey_action_revoke">La subclave será revocada</string>
|
||||
<string name="subkey_action_strip">La subclave será removida</string>
|
||||
|
|
|
@ -206,13 +206,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Lotu giltzak harremanekin izen eta post@ helbideetan ohinarrituz. Hau erabat lineaz-kanpo gertatzen da zure gailuan.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Giltza berriak ez dira harremanekin lotuko</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Berezgaitasunez eguneratu giltzak</string>
|
||||
<string name="label_experimental_settings_desc_title">Kontuz</string>
|
||||
<string name="label_experimental_settings_desc_title">Kontuz</string>
|
||||
<string name="label_experimental_settings_desc_summary">Ezaugarri hauek ez dute amaitu edo erabiltzaile jarduera/segurtasun emaitzak erdietsita. Honela, ez dira beren segurtasunean ohinarritzen eta mesedez ez jakinarazi aurkitzen dituzun arazoak!</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Jarri harremanetan keybase.io giltzak probatzeko eta erakutsi hauek giltza bat erakusten den bakoitzean</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Ikurrak eta ikusleiho asko oraindik ez daude azalgai ilunarekin zehaztuta)</string>
|
||||
<string name="label_settings_analytics_title">Ahalbidetu izengabeko estatistika erabilpena</string>
|
||||
<!--Proxy Preferences-->
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Gaitu Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Orbot ezarrita egotea behar du</string>
|
||||
<string name="pref_proxy_normal_title">Gaitu beste proxy bat</string>
|
||||
|
@ -1552,9 +1550,6 @@
|
|||
<string name="keylist_item_key_id">Giltza ID-a: %s</string>
|
||||
<string name="keylist_header_anonymous">Izengabe</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="button_analytics_yes">Bai, laguntzea nahi dut!</string>
|
||||
<string name="button_analytics_no">Ez, mila esker</string>
|
||||
<string name="snackbutton_analytics_settings">Ezarpenak</string>
|
||||
<string name="subkey_action_create">Azpigiltza sortuko da</string>
|
||||
<string name="subkey_action_revoke">Azpigiltza ukatuko da</string>
|
||||
<string name="subkey_action_expiry_never">Epemuga inoiz ez-ra aldatuko da</string>
|
||||
|
|
|
@ -430,6 +430,4 @@
|
|||
<string name="snack_keylist_clipboard_action">مشاهده</string>
|
||||
<string name="notify_title_keysync">درحال آپدیتکردن کلیدها...</string>
|
||||
<string name="keylist_header_anonymous">ناشناس</string>
|
||||
<string name="button_analytics_no">نه، ممنون</string>
|
||||
<string name="snackbutton_analytics_settings">تنظیمات</string>
|
||||
</resources>
|
||||
|
|
|
@ -381,5 +381,4 @@
|
|||
<string name="snack_keylist_clipboard_action">Näkymä</string>
|
||||
<string name="notify_title_keysync">Päivitetään avaimia...</string>
|
||||
<string name="keylist_header_anonymous">Anonyymi</string>
|
||||
<string name="snackbutton_analytics_settings">Asetukset</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Relier les clés aux contacts d’après les noms et les adresses courriel. Cela se passe entièrement hors ligne sur votre appareil.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Les nouvelles clés ne seront pas reliées aux contacts</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Mises à jour automatiques des clés</string>
|
||||
<string name="label_experimental_settings_desc_title">Avertissement</string>
|
||||
<string name="label_experimental_settings_desc_summary">Ces fonctions ne sont pas encore terminées et n’ont pas fait l’objet de recherche sur leur convivialité ni leur sécurité. Par conséquent, ne vous fiez pas à leur sécurité et veuillez ne pas signaler les problèmes que vous rencontrez.</string>
|
||||
<string name="label_experimental_settings_keybase_title">Preuves keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Contacter keybase.io pour obtenir des preuves de clé et les afficher chaque fois qu’une clé est affichée</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Les icônes et de nombreux écrans ne sont pas encore adaptés au thème sombre)</string>
|
||||
<string name="label_settings_analytics_title">Autoriser les statistiques anonymes d’utilisation</string>
|
||||
<string name="label_settings_analytics_summary">Si cette option est activée, des statistiques anonymes d\'utilisation sont envoyées pour aider à améliorer l’appli</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Activer Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Orbot doit être installé</string>
|
||||
|
@ -780,8 +777,6 @@
|
|||
<string name="my_keys">Mes clés</string>
|
||||
<string name="nav_backup">Sauvegarder/Restaurer</string>
|
||||
<string name="nav_transfer">Transfert Wi-Fi sécurisé</string>
|
||||
<string name="nav_shop">Boutique</string>
|
||||
<string name="nav_autocrypt">Essayer Autocrypt avec Thunderbird</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Saisir le texte</string>
|
||||
<!--certs-->
|
||||
|
@ -1806,12 +1801,6 @@
|
|||
<string name="keylist_item_key_id">ID de clé : %s</string>
|
||||
<string name="keylist_header_anonymous">Anonyme</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Pour améliorer l’expérience de tous les utilisateurs, OpenKeychain peut-elle recueillir des statistiques anonymes d’utilisation ?\n\nPour en apprendre davantage, consultez notre <a href="https://openkeychain.org/help/privacy-policy">politique de confidentialité</a>.</string>
|
||||
<string name="button_analytics_yes">Oui, je souhaite aider</string>
|
||||
<string name="button_analytics_no">Non, merci</string>
|
||||
<string name="snack_analytics_accept">Nous vous remercions de votre aide. Vous pouvez changer cette préférence dans vos paramètres.</string>
|
||||
<string name="snack_analytics_reject">Très bien, nous ne vous le demanderons plus. Vous pouvez changer cette préférence dans les paramètres.</string>
|
||||
<string name="snackbutton_analytics_settings">Paramètres</string>
|
||||
<string name="subkey_action_create">Une sous-clé sera créée</string>
|
||||
<string name="subkey_action_revoke">La sous-clé sera révoquée</string>
|
||||
<string name="subkey_action_strip">La sous-clé sera dépouillée</string>
|
||||
|
@ -1819,6 +1808,4 @@
|
|||
<string name="subkey_action_expiry_date">La date d’expiration sera changé à %s</string>
|
||||
<string name="share_key_clipboard">Partager la clé avec le presse-papiers</string>
|
||||
<string name="share_key">Partager la clé</string>
|
||||
<string name="shop_loading_label">Chargement de la boutique…</string>
|
||||
<string name="shop_title">Clés de sécurité COTECH</string>
|
||||
</resources>
|
||||
|
|
|
@ -211,14 +211,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Ligar chaves a contactos baseándose en nomes e enderezos de correo electrónico. Isto acontece sen precisar conexión a rede no seu dispositivo.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">As novas chaves non se ligarán aos contactos</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Actualización automática de chaves</string>
|
||||
<string name="label_experimental_settings_desc_title">Aviso</string>
|
||||
<string name="label_experimental_settings_desc_title">Aviso</string>
|
||||
<string name="label_experimental_settings_desc_summary">Estas características aínda non están rematadas ou son resultado da investigación da experiencia de usuaria e/ou seguridade. Xa que logo, non confíe na súa seguridade e por favor non informe sobre os fallos que poida atopar!</string>
|
||||
<string name="label_experimental_settings_keybase_title">probas de Keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Pedirlle probas de chave a keybase.io e expoñelas cada vez que se mostra a chave</string>
|
||||
<string name="label_experimental_settings_theme_summary">(As iconas e moitas pantallas non están aínda ben axustadas ao decorado oscuro)</string>
|
||||
<string name="label_settings_analytics_title">Permitir estátisticas anónimas sobre o uso</string>
|
||||
<string name="label_settings_analytics_summary">Se está habilitado, envía estatísticas anónimas sobre o uso para axudar a mellorar a app.</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Habilitar Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Precisa instalar Orbot</string>
|
||||
|
@ -1487,5 +1484,4 @@
|
|||
<string name="keylist_item_key_id">ID da Chave: %s</string>
|
||||
<string name="keylist_header_anonymous">Anónimo</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="snackbutton_analytics_settings">Axustes</string>
|
||||
</resources>
|
||||
|
|
|
@ -295,5 +295,4 @@
|
|||
<string name="token_unlock_ok">લોક ખોલો</string>
|
||||
<string name="select_identity_cancel">નિષ્ક્રિય કરો</string>
|
||||
<string name="key_gen_back">પહેલાનું</string>
|
||||
<string name="snackbutton_analytics_settings">સેટિંગ્સ</string>
|
||||
</resources>
|
||||
|
|
|
@ -214,6 +214,4 @@
|
|||
<string name="key_gen_back">पिछला</string>
|
||||
<string name="key_gen_finish">समाप्त</string>
|
||||
<string name="snack_keylist_clipboard_action">राय</string>
|
||||
<string name="button_analytics_no">जी नहीं, धन्यवाद</string>
|
||||
<string name="snackbutton_analytics_settings">सेटिंग्स</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Kulcsok összekapcsolása partnerekkel a nevük és az e-mail címük alapján. Ez teljesen mértékben hálózati kapcsolat nélkül történik az ön eszközén.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Az új kulcsok nem lesznek összekapcsolva partnerekkel</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatikus kulcsfrissítések</string>
|
||||
<string name="label_experimental_settings_desc_title">Figyelmeztetés</string>
|
||||
<string name="label_experimental_settings_desc_summary">Ezek a funkciók még nincsenek befejezve, vagy felhasználói élmény és biztonsági kutatást eredményei. Emiatt ne bízzon a biztonságukban, és ne jelentse a felmerülő hibákat!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io igazolások</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Kapcsolatfelvétel a keybase.io-val a kulcsigazolásokért, és megtekintés minden alkalommal, amikor egy kulcs megjelenik</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Az ikonok és számos képernyő még nem lett hozzáigazítva a sötét témához)</string>
|
||||
<string name="label_settings_analytics_title">Névtelen használati statisztikák engedélyezése</string>
|
||||
<string name="label_settings_analytics_summary">Ha engedélyezve van, akkor névtelen használati statisztikákat küld, hogy segítsen az alkalmazás továbbfejlesztésében </string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Tor engedélyezése</string>
|
||||
<string name="pref_proxy_tor_summary">Az Orbot telepítése szükséges</string>
|
||||
|
@ -743,15 +740,10 @@
|
|||
<string name="keylist_item_key_id">Kulcsazonosító: %s</string>
|
||||
<string name="keylist_header_anonymous">Anoním / Névtelen</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="button_analytics_yes">Igen, szeretnék segíteni!</string>
|
||||
<string name="button_analytics_no">Köszönöm, nem</string>
|
||||
<string name="snackbutton_analytics_settings">Beállítások</string>
|
||||
<string name="subkey_action_create">Az alkulcs létrehozásra kerül</string>
|
||||
<string name="subkey_action_revoke">Az alkulcs visszavonásra kerül</string>
|
||||
<string name="subkey_action_expiry_never">A lejárat sohára változik</string>
|
||||
<string name="subkey_action_expiry_date">A lejárat %s értékre változik</string>
|
||||
<string name="share_key_clipboard">Kulcs megosztása vágólappal</string>
|
||||
<string name="share_key">Kulcs megosztása</string>
|
||||
<string name="shop_loading_label">Shop betöltése...</string>
|
||||
<string name="shop_title">COTECH biztonsági kulcsok</string>
|
||||
</resources>
|
||||
|
|
|
@ -249,5 +249,4 @@
|
|||
<string name="snack_keylist_clipboard_action">Lihat</string>
|
||||
<string name="keylist_header_anonymous">Anonim</string>
|
||||
<string name="keylist_header_special"># </string>
|
||||
<string name="snackbutton_analytics_settings">Pengaturan</string>
|
||||
</resources>
|
||||
|
|
|
@ -194,8 +194,7 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Collega le chiavi ai contatti in base ai nomi e indirizzi email. Ciò avviene completamente offline sul tuo dispositivo.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Le chiavi nuove non verranno collegate ai contatti</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Aggiornamenti automatici delle chiavi</string>
|
||||
<string name="label_experimental_settings_desc_title">Attenzione</string>
|
||||
<string name="label_experimental_settings_desc_title">Attenzione</string>
|
||||
<string name="label_experimental_settings_desc_summary">Queste funzioni non sono ancora finite o testate da utenti/ricerche di sicurezza. Perciò non contare sulla loro sicurezza e per favore non segnalare i problemi che riscontri!</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Contatta keybase.io per avere prove e mostrale ogni volta che visualizzo una chiave</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Le icone e molte schermate non sono ancora modificate correttamente per il tema scuro)</string>
|
||||
|
@ -1177,6 +1176,4 @@
|
|||
<string name="keylist_item_key_id">ID chiave: %s</string>
|
||||
<string name="keylist_header_anonymous">Anonimo</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="button_analytics_no">No, grazie</string>
|
||||
<string name="snackbutton_analytics_settings">Impostazioni</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">オフランで完結して、名前とメールアドレスに基づいて、鍵を連絡先にリンク</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">新しい鍵は連絡先と関連付けしない</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">鍵の自動アップデート</string>
|
||||
<string name="label_experimental_settings_desc_title">注意</string>
|
||||
<string name="label_experimental_settings_desc_summary">これらの機能はまだ完成していないか、ユーザーエクスペリエンス/セキュリティ研究の結果ではありません。そのため、このセキュリティに依存したり、遭遇した問題を報告しないでください!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io 検証</string>
|
||||
<string name="label_experimental_settings_keybase_summary">鍵の検証のためkeybase.ioに連絡し、鍵が表示されるたびに表示します</string>
|
||||
<string name="label_experimental_settings_theme_summary">(アイコンおよび多くの画面は、まだダークテーマに応じて調整されていません)</string>
|
||||
<string name="label_settings_analytics_title">匿名の使用統計データの送信を許可する</string>
|
||||
<string name="label_settings_analytics_summary">有効にすると、アプリの改善に役立つ匿名の使用統計データが送信されます</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Torを有効</string>
|
||||
<string name="pref_proxy_tor_summary">Orbotのインストールが要求されます</string>
|
||||
|
@ -766,8 +763,6 @@
|
|||
<string name="my_keys">自分の鍵</string>
|
||||
<string name="nav_backup">バックアップ/リストア</string>
|
||||
<string name="nav_transfer">安全な Wifi 転送</string>
|
||||
<string name="nav_shop">ショップ</string>
|
||||
<string name="nav_autocrypt">Thunderbird と Autocrypt をお試しください</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">テキストを入力</string>
|
||||
<!--certs-->
|
||||
|
@ -1776,12 +1771,6 @@
|
|||
<string name="keylist_item_key_id">鍵 ID: %s</string>
|
||||
<string name="keylist_header_anonymous">匿名</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">すべてのユーザーのエクスペリエンスを改善するために、OpenKeychainの匿名の使用統計データを送信していただけますか?\n\n詳細については、<a href="https://openkeychain.org/help/privacy-policy">プライバシーポリシー</a>をご覧ください。</string>
|
||||
<string name="button_analytics_yes">はい、匿名の使用統計データを送信して応援します!</string>
|
||||
<string name="button_analytics_no">いいえ、使用統計データを送信しません。</string>
|
||||
<string name="snack_analytics_accept">応援をありがとうございます! この設定は「設定」で変更できます。</string>
|
||||
<string name="snack_analytics_reject">大丈夫です、今後お尋ねしないようにします。「設定」でこの設定を変更できます。</string>
|
||||
<string name="snackbutton_analytics_settings">設定</string>
|
||||
<string name="subkey_action_create">サブキーが作成されます</string>
|
||||
<string name="subkey_action_revoke">サブキーは取り消されます</string>
|
||||
<string name="subkey_action_strip">サブキーは削除されます</string>
|
||||
|
@ -1789,6 +1778,4 @@
|
|||
<string name="subkey_action_expiry_date">有効期限は%sに変更されます</string>
|
||||
<string name="share_key_clipboard">クリップボードでキーを共有する</string>
|
||||
<string name="share_key">鍵の共有</string>
|
||||
<string name="shop_loading_label">ショップを読み込んでいます…</string>
|
||||
<string name="shop_title">COTECH セキュリティ鍵</string>
|
||||
</resources>
|
||||
|
|
|
@ -202,13 +202,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">이름과 이메일 주소에 기초해 키를 연락처에 연결합니다. 이 작업은 완전히 오프라인에서만 수행됩니다.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">새 키를 앞으로 연락처에 연결하지 않음</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">자동 키 갱신</string>
|
||||
<string name="label_experimental_settings_desc_title">경고</string>
|
||||
<string name="label_experimental_settings_desc_title">경고</string>
|
||||
<string name="label_experimental_settings_desc_summary">이 기능들은 아직 완성되지 않았거나 유저 경험/보안 연구의 결과가 아닙니다. 그러니 이 기능의 보안성에 의존하거나 겪는 문제를 보고하지 말아주세요!</string>
|
||||
<string name="label_experimental_settings_keybase_summary">keybase.io에 연결해서 키가 표시될 때 마다 키 증명을 표시</string>
|
||||
<string name="label_experimental_settings_theme_summary">(아이콘이나 많은 화면이 검은 테마에 알맞게 아직 조정되지 않았습니다)</string>
|
||||
<string name="label_settings_analytics_title">익명 사용 통계 허가</string>
|
||||
<!--Proxy Preferences-->
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Tor 활성화</string>
|
||||
<string name="pref_proxy_tor_summary">Orbot이 설치되어 있어야 합니다.</string>
|
||||
<string name="pref_proxy_normal_title">다른 프록시 활성화</string>
|
||||
|
@ -896,10 +894,4 @@
|
|||
<string name="snack_keylist_clipboard_action">보기</string>
|
||||
<string name="notify_title_keysync">키를 업데이트 중...</string>
|
||||
<string name="keylist_header_anonymous">익명</string>
|
||||
<string name="dialog_analytics_consent">모든 사용자의 경험을 개선하기 위해서 OpenKeychain가 익명 사용 데이터를 수집하는걸 허가 하시겠습니까?\n\n더 많은 정보는 저희의 <a href="https://openkeychain.org/help/privacy-policy">개인정보 보호정책</a>을 참고하십시오.</string>
|
||||
<string name="button_analytics_yes">예, 돕겠습니다!</string>
|
||||
<string name="button_analytics_no">아니요</string>
|
||||
<string name="snack_analytics_accept">도와주셔서 감사합니다! 설정에서 언제든지 변경하실 수 있습니다.</string>
|
||||
<string name="snack_analytics_reject">괜찮습니다. 이제 표시하지 않습니다. 마음이 바뀌신다면 설정에서 변경하실 수 있습니다.</string>
|
||||
<string name="snackbutton_analytics_settings">설정</string>
|
||||
</resources>
|
||||
|
|
|
@ -164,5 +164,4 @@
|
|||
<string name="select_identity_cancel">പ്രവർത്തനരഹിതമാക്കു </string>
|
||||
<string name="key_gen_back">പിന്നോട്ട്</string>
|
||||
<string name="snack_keylist_clipboard_action">കാണുക</string>
|
||||
<string name="snackbutton_analytics_settings">സെറ്റിംഗ്സ് </string>
|
||||
</resources>
|
||||
|
|
|
@ -202,8 +202,7 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Lenk nøkler til kontakter basert på navn og e-postadresser. Dette skjer helt frakoblet på din enhet.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nye nøkler vil ikke lenkes til kontakter</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatiske nøkkeloppdateringer</string>
|
||||
<string name="label_experimental_settings_desc_title">Advarsel</string>
|
||||
<string name="label_experimental_settings_desc_title">Advarsel</string>
|
||||
<string name="label_experimental_settings_desc_summary">Disse funksjonene er ikke ferdige enda, eller resultat av brukererfaring/sikkerhetsforskning. Som sådan, bør du ikke stole på at de er sikre, og ikke rapportere feil du kommer over!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io-bevis</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Kontakt keybase.io for nøkkelbevis og vis dem hver gang en nøkkel vises</string>
|
||||
|
@ -869,6 +868,4 @@
|
|||
<string name="notify_title_keysync">oppdaterer nøkler…</string>
|
||||
<string name="keylist_item_key_id">Nøkkel-ID: %s</string>
|
||||
<string name="keylist_header_anonymous">Anonym</string>
|
||||
<string name="button_analytics_no">Nei takk</string>
|
||||
<string name="snackbutton_analytics_settings">Innstillinger</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Koppel sleutels aan contacten gebaseerd op namen en e-mailadressen. Dit gebeurt volledig offline op je apparaat.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nieuwe sleutels zullen niet worden gekoppeld aan contacten</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatische sleutelupdates</string>
|
||||
<string name="label_experimental_settings_desc_title">Waarschuwing</string>
|
||||
<string name="label_experimental_settings_desc_title">Waarschuwing</string>
|
||||
<string name="label_experimental_settings_desc_summary">Deze functies zijn nog niet afgewerkt of zijn resultaten van gebruikerservaringen/beveiligingsonderzoek. Vertrouw daarom niet op hun beveiliging en rapporteer geen problemen die je ervaart!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io-bewijzen</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Maak contact met keybase.io voor bevestigingen van sleutels en toon deze telkens wanneer een sleutel wordt weergegeven</string>
|
||||
<string name="label_experimental_settings_theme_summary">(De pictogrammen en veel schermen zijn nog niet geoptimaliseerd voor het donkere thema)</string>
|
||||
<string name="label_settings_analytics_title">Anonieme gebruikersstatistieken toestaan</string>
|
||||
<string name="label_settings_analytics_summary">Indien ingeschakeld worden er anonieme gebruikersstatistieken verzonden om de app te verbeteren</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Tor inschakelen</string>
|
||||
<string name="pref_proxy_tor_summary">Vereist dat Orbot geïnstalleerd is</string>
|
||||
|
@ -1804,12 +1801,6 @@
|
|||
<string name="keylist_item_key_id">Sleutel-ID: %s</string>
|
||||
<string name="keylist_header_anonymous">Anoniem</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Mag OpenKeychain gebruikersstatistieken verzamelen om de ervaring voor alle gebruikers te verbeteren?\n\nVoor meer informatie, zie ons <a href="https://openkeychain.org/help/privacy-policy">privacybeleid</a>.</string>
|
||||
<string name="button_analytics_yes">Ja, ik wil helpen!</string>
|
||||
<string name="button_analytics_no">Nee, bedankt</string>
|
||||
<string name="snack_analytics_accept">Bedankt voor de hulp! Je kan deze voorkeur veranderen in de instellingen.</string>
|
||||
<string name="snack_analytics_reject">Geen probleem, we zullen het niet opnieuw vragen. Je kan van gedachten veranderen in de instellingen.</string>
|
||||
<string name="snackbutton_analytics_settings">Instellingen</string>
|
||||
<string name="subkey_action_create">Subsleutel zal worden aangemaakt</string>
|
||||
<string name="subkey_action_revoke">Subsleutel zal worden ingetrokken</string>
|
||||
<string name="subkey_action_strip">Subsleutel zal worden gestript</string>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Koppel sleutels aan contacten gebaseerd op namen en e-mailadressen. Dit gebeurt volledig offline op je apparaat.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nieuwe sleutels zullen niet worden gekoppeld aan contacten</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatische sleutelupdates</string>
|
||||
<string name="label_experimental_settings_desc_title">Waarschuwing</string>
|
||||
<string name="label_experimental_settings_desc_summary">Deze functies zijn nog niet afgewerkt of zijn resultaten van gebruikerservaringen/beveiligingsonderzoek. Vertrouw daarom niet op hun beveiliging en rapporteer geen problemen die je ervaart!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io-bewijzen</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Maak contact met keybase.io voor bevestigingen van sleutels en weergeef dit elke keer als een sleutel wordt weer gegeven</string>
|
||||
<string name="label_experimental_settings_theme_summary">(De pictogrammen en veel schermen zijn nog niet geoptimaliseerd voor het donkere thema)</string>
|
||||
<string name="label_settings_analytics_title">Sta anonieme gebruikersstatistieken toe</string>
|
||||
<string name="label_settings_analytics_summary">Indien ingeschakeld worden er anonieme gebruikersstatistieken verzonden om de app te verbeteren</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Tor inschakelen</string>
|
||||
<string name="pref_proxy_tor_summary">Vereist dat Orbot geïnstalleerd is</string>
|
||||
|
@ -780,8 +777,6 @@
|
|||
<string name="my_keys">Mijn sleutels</string>
|
||||
<string name="nav_backup">Back-up/herstellen</string>
|
||||
<string name="nav_transfer">Beveiligde wifi-overdracht</string>
|
||||
<string name="nav_shop">Winkel</string>
|
||||
<string name="nav_autocrypt">Probeer Autocrypt met Thunderbird</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Voer tekst in</string>
|
||||
<!--certs-->
|
||||
|
@ -1806,12 +1801,6 @@
|
|||
<string name="keylist_item_key_id">Sleutel-ID: %s</string>
|
||||
<string name="keylist_header_anonymous">Anoniem</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Mag OpenKeychain gebruikersstatistieken verzamelen om de ervaring voor alle gebruikers te verbeteren?\n\nVoor meer informatie, zie ons <a href="https://openkeychain.org/help/privacy-policy">privacybeleid</a>.</string>
|
||||
<string name="button_analytics_yes">Ja, ik wil meehelpen!</string>
|
||||
<string name="button_analytics_no">Nee, bedankt</string>
|
||||
<string name="snack_analytics_accept">Bedankt voor de hulp! Je kan deze voorkeur veranderen in de instellingen.</string>
|
||||
<string name="snack_analytics_reject">Geen probleem, we zullen het niet opnieuw vragen. Je kan van gedachten veranderen in de instellingen.</string>
|
||||
<string name="snackbutton_analytics_settings">Instellingen</string>
|
||||
<string name="subkey_action_create">Subsleutel zal worden aangemaakt</string>
|
||||
<string name="subkey_action_revoke">Subsleutel zal worden ingetrokken</string>
|
||||
<string name="subkey_action_strip">Subsleutel zal worden gestript</string>
|
||||
|
@ -1819,6 +1808,4 @@
|
|||
<string name="subkey_action_expiry_date">Verloopdatum zal wijzigen naar %s</string>
|
||||
<string name="share_key_clipboard">Sleutel delen via klembord</string>
|
||||
<string name="share_key">Sleutel delen</string>
|
||||
<string name="shop_loading_label">Winkel aan het laden…</string>
|
||||
<string name="shop_title">COTECH-beveiligingssleutels</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Połącz klucze z kontaktami używając nazw i adresów e-mail. Ta operacja jest wykonywana offline wyłączenie na tym urządzeniu.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nowe klucze nie będą połączone z kontaktami</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatyczne aktualizacje kluczy</string>
|
||||
<string name="label_experimental_settings_desc_title">Ostrzeżenie</string>
|
||||
<string name="label_experimental_settings_desc_title">Ostrzeżenie</string>
|
||||
<string name="label_experimental_settings_desc_summary">Te funkcje nie są jeszcze skończone lub są wynikiem badań bezpieczeństwa / wrażeń użytkownika. Tak więc nie polegaj na ich bezpieczeństwie i nie zgłaszaj problemów z nimi związanych!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Dowody keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Sprawdź keybase.io dla dowodów kluczy i pokaż je za każdym razem kiedy klucz jest wyświetlany</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Ikony i ekrany nie są jeszcze dostosowane do ciemnego motywu)</string>
|
||||
<string name="label_settings_analytics_title">Zezwalaj na anonimowe statystyki użycia</string>
|
||||
<string name="label_settings_analytics_summary">Jeśli jest włączone wysyła anonimowe statystyki użycia aby pomóc w ulepszaniu aplikacji</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Włącz Tora</string>
|
||||
<string name="pref_proxy_tor_summary">Wymaga Orbota</string>
|
||||
|
@ -1872,12 +1869,6 @@
|
|||
<string name="keylist_item_key_id">ID Klucza: %s</string>
|
||||
<string name="keylist_header_anonymous">Anonimowy</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Czy aby polepszyć doświadczenia użytkowników, OpenKeychain może zbierać anonimowe dane użytkowania?\n\nAby dowiedzieć się więcej zobacz naszą <a href="https://openkeychain.org/help/privacy-policy">Politykę Prywatności</a>.</string>
|
||||
<string name="button_analytics_yes">Tak, chcę pomóc!</string>
|
||||
<string name="button_analytics_no">Nie, dzięki</string>
|
||||
<string name="snack_analytics_accept">Dziękujemy za pomoc! Możesz zmienić to ustawienie w opcjach.</string>
|
||||
<string name="snack_analytics_reject">Dobrze, nie zapytamy więcej. Możesz zmienić to ustawienie w opcjach.</string>
|
||||
<string name="snackbutton_analytics_settings">Ustawienia</string>
|
||||
<string name="subkey_action_create">Podklucz zostanie utworzony</string>
|
||||
<string name="subkey_action_revoke">Podklucz zostanie unieważniony</string>
|
||||
<string name="subkey_action_strip">Podklucz zostanie wyczyszony</string>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Vincula chaves a contatos com base em nomes e endereços de email. Isto acontece completamente off-line em seu dispositivo.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Novas chaves não serão vinculadas aos contatos</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Atualização automática de chaves</string>
|
||||
<string name="label_experimental_settings_desc_title">Aviso</string>
|
||||
<string name="label_experimental_settings_desc_title">Aviso</string>
|
||||
<string name="label_experimental_settings_desc_summary">Esses recursos ainda não estão finalizados ou resultam de pesquisas de experiência/segurança do usuário. Dessa forma, não confie em sua segurança e, por favor, não relate problemas que encontre!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Provas Keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Contate o keybase.io para as provas de chaves e mostre-as toda vez que uma chave for exibida</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Os ícones e algumas telas ainda não foram adaptados para o tema escuro)</string>
|
||||
<string name="label_settings_analytics_title">Permitir estatísticas anônimas de uso</string>
|
||||
<string name="label_settings_analytics_summary">Se habilitado, envia estatísticas anônimas de uso para ajudar a melhorar a aplicação</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Ativar Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Requer que o Orbot esteja instalado</string>
|
||||
|
@ -1804,12 +1801,6 @@
|
|||
<string name="keylist_item_key_id">ID da Chave: %s</string>
|
||||
<string name="keylist_header_anonymous">Anônimo</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Para melhorar a experiência para todos os usuários, o OpenKeychain pode coletar estatísticas anônimas de uso?\n\nPara saber mais, consulte nossa <a href="https://openkeychain.org/help/privacy-policy">Política de Privacidade</a>.</string>
|
||||
<string name="button_analytics_yes">Sim, eu quero ajudar!</string>
|
||||
<string name="button_analytics_no">Não, obrigado</string>
|
||||
<string name="snack_analytics_accept">Obrigado por nos ajudar! Você pode alterar esta preferência nas configurações.</string>
|
||||
<string name="snack_analytics_reject">Sem problema, não iremos pedir de novo. Você pode mudar de ideia nas configurações.</string>
|
||||
<string name="snackbutton_analytics_settings">Configurações</string>
|
||||
<string name="subkey_action_create">A subchave será criada</string>
|
||||
<string name="subkey_action_revoke">A subchave será revogada</string>
|
||||
<string name="subkey_action_strip">A subchave será extirpada</string>
|
||||
|
|
|
@ -417,5 +417,4 @@
|
|||
<string name="snack_keylist_clipboard_action">Vizualizare</string>
|
||||
<string name="keylist_header_anonymous">Anonim</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="snackbutton_analytics_settings">Setări</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Связывать ключи с контактами основываясь на именах и адресах электронной почты. Это происходит полностью в автономном режиме на вашем устройстве.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Новые ключи не будут связаны с контактами</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Автообновление ключей</string>
|
||||
<string name="label_experimental_settings_desc_title">Предупреждение</string>
|
||||
<string name="label_experimental_settings_desc_summary">Эти новые возможности ещё не закончены и/или пока только изучаются. Проще говоря, не стоит полагаться на их безопасность. Пожалуйста, не сообщайте о связанных с ними проблемах!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Подтверждение Keybase.io </string>
|
||||
<string name="label_experimental_settings_keybase_summary">Опрашивать keybase.io для подтверждения ключей и показывать это каждый раз при отображении ключей</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Значки и многие экраны ещё не скорректированы для тёмной темы)</string>
|
||||
<string name="label_settings_analytics_title">Отправлять анонимную статистику</string>
|
||||
<string name="label_settings_analytics_summary">Если данная функция включена, приложение будет отправлять анонимную статистику для помощи разработчикам</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Использовать Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Требуется установка Orbot</string>
|
||||
|
@ -817,8 +814,6 @@
|
|||
<string name="my_keys">Мои ключи</string>
|
||||
<string name="nav_backup">Резервирование/Восстановление</string>
|
||||
<string name="nav_transfer">Безопасная передача по Wi-Fi</string>
|
||||
<string name="nav_shop">Магазин</string>
|
||||
<string name="nav_autocrypt">Попробуйте Autocrypt с Thunderbird</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Напишите текст</string>
|
||||
<!--certs-->
|
||||
|
@ -1875,12 +1870,6 @@
|
|||
<string name="keylist_item_key_id">ID ключа: %s</string>
|
||||
<string name="keylist_header_anonymous">Аноним</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Может ли OpenKeychain собирать анонимную статистику для улучшения работы приложения?\n\nЧтобы узнать больше, изучите нашу <a href="https://openkeychain.org/help/privacy-policy">Политику конфиденциальности</a>.</string>
|
||||
<string name="button_analytics_yes">Да, я хочу помочь!</string>
|
||||
<string name="button_analytics_no">Нет, спасибо</string>
|
||||
<string name="snack_analytics_accept">Спасибо за помощь! Вы можете изменить данную функцию в настройках.</string>
|
||||
<string name="snack_analytics_reject">Всё нормально, больше мы не будем спрашивать. Вы можете изменить своё мнение с помощью настроек.</string>
|
||||
<string name="snackbutton_analytics_settings">Настройки</string>
|
||||
<string name="subkey_action_create">Доп. ключ будет создан</string>
|
||||
<string name="subkey_action_revoke">Доп. ключ будет отозван</string>
|
||||
<string name="subkey_action_strip">Доп. ключ будет отделён</string>
|
||||
|
@ -1888,6 +1877,4 @@
|
|||
<string name="subkey_action_expiry_date">Срок годности будет изменён на %s</string>
|
||||
<string name="share_key_clipboard">Отправить ключ в буфер обмена</string>
|
||||
<string name="share_key">Отправить ключ</string>
|
||||
<string name="shop_loading_label">Загрузка магазина...</string>
|
||||
<string name="shop_title">Ключи безопасности COTECH</string>
|
||||
</resources>
|
||||
|
|
|
@ -831,6 +831,5 @@
|
|||
<string name="key_gen_finish">Končaj</string>
|
||||
<string name="notify_channel_passcache">Hranjenje gesla v spominu</string>
|
||||
<string name="notify_title_keysync">Posodabljam ključe...</string>
|
||||
<string name="snackbutton_analytics_settings">Nastavitve</string>
|
||||
<string name="share_key">Deli ključ</string>
|
||||
<string name="share_key">Deli ključ</string>
|
||||
</resources>
|
||||
|
|
|
@ -188,8 +188,7 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Повезивање кључева са контактима на основу имена и е-адреса. Одвија се у потпуности ван везе на вашем уређају.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Нови кључеви неће бити повезани са контактима</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Аутоматско ажурирање кључева</string>
|
||||
<string name="label_experimental_settings_desc_title">Упозорење</string>
|
||||
<string name="label_experimental_settings_desc_title">Упозорење</string>
|
||||
<string name="label_experimental_settings_desc_summary">Ове функције још нису завршене или су резултат истраживања корисничког искуства и безбедности. Стога се не ослањајте на њихову безбедност и молимо вас да не пријављујете грешке на које наиђете!</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Тражење доказа кључева на keybase.io и њихов приказ приликом приказивања кључа</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Иконе и многи екрани још нису прилагођени за тамну тему)</string>
|
||||
|
@ -1449,5 +1448,4 @@
|
|||
<string name="notify_channel_passcache">Кеширај лозинку</string>
|
||||
<string name="notify_title_keysync">Ажурирам кључеве…</string>
|
||||
<string name="keylist_item_key_id">ИД кључа: %s</string>
|
||||
<string name="snackbutton_analytics_settings">Podešavanja</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Länka nycklar till kontakter baserade på namn och e-postadresser. Detta sker helt offline på enheten.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Nya nycklar kommer inte att kopplas till kontakter</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Automatiska nyckeluppdateringar</string>
|
||||
<string name="label_experimental_settings_desc_title">Varning</string>
|
||||
<string name="label_experimental_settings_desc_summary">Dessa funktioner är ännu inte färdiga eller resultat av användarupplevelse/säkerhetsforskning. Lita därför inte på deras säkerhet och var snäll och rapportera inte problem du stöter på!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io proofs</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Kontakta keybase.io för nyckelbevis och visa dem varje gång en nyckel visas</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Ikonerna och många skärmar är ännu inte justerade för mörkt tema)</string>
|
||||
<string name="label_settings_analytics_title">Tillåt anonym användarstatistik</string>
|
||||
<string name="label_settings_analytics_summary">Om den är aktiverad skickar du anonym användarstatistik för att förbättra appen</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Aktivera Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Kräver att Orbot är installerad</string>
|
||||
|
@ -776,8 +773,6 @@
|
|||
<string name="my_keys">Mina nycklar</string>
|
||||
<string name="nav_backup">Säkerhetskopiera/återställa</string>
|
||||
<string name="nav_transfer">Säker Wi-Fi-överföring</string>
|
||||
<string name="nav_shop">Butik</string>
|
||||
<string name="nav_autocrypt">Prova Autocrypt med Thunderbird</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">Skriv text</string>
|
||||
<!--certs-->
|
||||
|
@ -1670,12 +1665,6 @@
|
|||
<string name="keylist_item_key_id">Nyckel-ID: %s</string>
|
||||
<string name="keylist_header_anonymous">Anonym</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">För att förbättra upplevelsen för alla användare, kan OpenKeychain samla anonym användningsstatistik?\n\nFör mer information, se vår <a href="https://openkeychain.org/help/privacy-policy">integritetspolicy</a>.</string>
|
||||
<string name="button_analytics_yes">Ja, jag vill hjälpa till!</string>
|
||||
<string name="button_analytics_no">Nej tack</string>
|
||||
<string name="snack_analytics_accept">Tack för hjälpen! Du kan ändra denna inställning i inställningarna.</string>
|
||||
<string name="snack_analytics_reject">Det är okej, vi kommer inte att fråga igen. Du kan ändra dig i inställningarna.</string>
|
||||
<string name="snackbutton_analytics_settings">Inställningar</string>
|
||||
<string name="subkey_action_create">Undernyckel kommer att skapas</string>
|
||||
<string name="subkey_action_revoke">Undernyckel kommer att återkallas</string>
|
||||
<string name="subkey_action_strip">Undernyckeln kommer att avskalas</string>
|
||||
|
|
|
@ -127,5 +127,4 @@
|
|||
<string name="button_cancel">రద్దు</string>
|
||||
<string name="transfer_status_connected">సంబంధిత</string>
|
||||
<string name="transfer_confirm_cancel">రద్దు</string>
|
||||
<string name="snackbutton_analytics_settings">సెట్టింగులు</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,5 +212,4 @@
|
|||
<string name="key_gen_back">ย้อนกลับ</string>
|
||||
<string name="key_gen_finish">เสร็จ</string>
|
||||
<string name="snack_keylist_clipboard_action">ดู</string>
|
||||
<string name="snackbutton_analytics_settings">การตั้งค่า</string>
|
||||
</resources>
|
||||
|
|
|
@ -523,6 +523,4 @@
|
|||
<string name="snack_keylist_clipboard_action">Göster</string>
|
||||
<string name="notify_channel_passcache">Parola önbelleği</string>
|
||||
<string name="keylist_header_anonymous">Anonim</string>
|
||||
<string name="button_analytics_no">Hayır, teşekkürler</string>
|
||||
<string name="snackbutton_analytics_settings">Ayarlar</string>
|
||||
</resources>
|
||||
|
|
|
@ -212,15 +212,12 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Прив\'язувати ключі до контактів на основі імен та адрес електронної пошти. Це відбувається повністю локально на Вашому пристрої.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Нові ключі не будуть прив\'язані до контактів</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Автоматичне оновлення ключів</string>
|
||||
<string name="label_experimental_settings_desc_title">Попередження</string>
|
||||
<string name="label_experimental_settings_desc_title">Попередження</string>
|
||||
<string name="label_experimental_settings_desc_summary">Ці можливості ще в розробці. Будь ласка, не сподівайтесь на їх безпеку та не повідомляйте про проблеми їхнього використання!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Перевірки Keybase.io</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Контактувати з keybase.io для підтвердження ключа та показувати дійсності щоразу, коли ключ відображено</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Піктограми та більшість екранів не налаштовані для темної теми)</string>
|
||||
<string name="label_settings_analytics_title">Дозволити анонімну статистику про використання</string>
|
||||
<string name="label_settings_analytics_summary">Якщо увімкнено, надсилає анонімну статистику про використання, щоби допомогти покращити цей застосунок</string>
|
||||
<!--Proxy Preferences-->
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">Увімкнути Tor</string>
|
||||
<string name="pref_proxy_tor_summary">Потребує інсталяції Orbot</string>
|
||||
<string name="pref_proxy_normal_title">Увімкнути інше проксі</string>
|
||||
|
@ -1876,13 +1873,7 @@
|
|||
<string name="keylist_item_key_id">Ідентифікатор ключа: %s</string>
|
||||
<string name="keylist_header_anonymous">Анонімно</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">Щоб покращити досвід для всіх користувачів, чи може OpenKeychain збирати анонімну статистику про використання?\n\nЩоби дізнатися більше, див. нашу <a href="https://openkeychain.org/help/privacy-policy">Політику конфіденційності</a>.</string>
|
||||
<string name="button_analytics_yes">Так, я хочу допомогти!</string>
|
||||
<string name="button_analytics_no">Ні, дякую</string>
|
||||
<string name="snack_analytics_accept">Дякуємо за допомогу! Ви можете змінити цей параметр у налаштуваннях.</string>
|
||||
<string name="snack_analytics_reject">Усе добре, ми не будемо запитувати вас знову. Ви можете змінити свою думку в налаштуваннях.</string>
|
||||
<string name="snackbutton_analytics_settings">Налаштування</string>
|
||||
<string name="subkey_action_create">Підключ буде створено</string>
|
||||
<string name="subkey_action_create">Підключ буде створено</string>
|
||||
<string name="subkey_action_revoke">Підключ буде відкликано</string>
|
||||
<string name="subkey_action_strip">Підключ буде відділено</string>
|
||||
<string name="subkey_action_expiry_never">Термін дії буде змінено на ніколи</string>
|
||||
|
|
|
@ -189,8 +189,7 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">Liên kết khóa với danh bạ dựa trên tên và địa chỉ thư. Nó được thực hiện hoàn toàn ngoại tuyến trên thiết bị của bạn.</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">Các khóa mới sẽ không được liên kết với danh bạ</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">Cập nhật khóa tự động</string>
|
||||
<string name="label_experimental_settings_desc_title">Cảnh báo</string>
|
||||
<string name="label_experimental_settings_desc_title">Cảnh báo</string>
|
||||
<string name="label_experimental_settings_desc_summary">Tính năng này hiện chưa được hoàn thành hoặc chỉ mới là kết quả nghiên cứu/thử nghiệm. Do vậy, bạn đừng tin cậy vào tính an toàn của chúng cũng như không cần thiết phải thông báo với chúng tôi!</string>
|
||||
<string name="label_experimental_settings_keybase_summary">Liên hệ với keybase.io để minh chứng khóa và hiển thị chúng mỗi khi khóa được gọi</string>
|
||||
<string name="label_experimental_settings_theme_summary">(Các biểu tượng và nhiều màn hình không được thêm vào giao diện dạng tối)</string>
|
||||
|
@ -371,5 +370,4 @@
|
|||
<string name="key_gen_finish">Kết thúc</string>
|
||||
<string name="snack_keylist_clipboard_action">Xem</string>
|
||||
<string name="keylist_header_anonymous">Ẩn danh</string>
|
||||
<string name="snackbutton_analytics_settings">Thiết đặt</string>
|
||||
</resources>
|
||||
|
|
|
@ -213,11 +213,9 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">連接金鑰至聯絡人(基於姓名和電子郵件地址)。這只在您的裝置上離線進行。</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">新的金鑰將不會與聯絡人連接</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">自動金鑰同步</string>
|
||||
<string name="label_experimental_settings_desc_title">警告</string>
|
||||
<string name="label_experimental_settings_desc_title">警告</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io 證明</string>
|
||||
<string name="label_settings_analytics_title">允許匿名的使用資料收集</string>
|
||||
<!--Proxy Preferences-->
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">啟用洋蔥(Tor)網路</string>
|
||||
<string name="pref_proxy_tor_summary">必須已安裝 Orbot</string>
|
||||
<string name="pref_proxy_normal_title">啟用其他代理伺服器</string>
|
||||
|
@ -967,9 +965,6 @@
|
|||
<string name="keylist_item_key_id">金鑰 ID: %s</string>
|
||||
<string name="keylist_header_anonymous">匿名</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="button_analytics_yes">是的,我想協助!</string>
|
||||
<string name="button_analytics_no">不用,謝謝</string>
|
||||
<string name="snackbutton_analytics_settings">設定</string>
|
||||
<string name="subkey_action_create">子金鑰將被創建</string>
|
||||
<string name="subkey_action_revoke">子金鑰將被撤銷</string>
|
||||
<string name="subkey_action_strip">子金鑰將被分離</string>
|
||||
|
|
|
@ -212,14 +212,11 @@
|
|||
<string name="label_sync_settings_contacts_summary_on">根据名字和邮件地址将密钥关联到联系人。这是本地操作,不需要设备联网。</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">新密钥将不会与联系人相关联</string>
|
||||
<!--label shown in Android settings under the OpenKeychain account-->
|
||||
<string name="keyserver_sync_settings_title">自动更新密钥</string>
|
||||
<string name="label_experimental_settings_desc_title">警告</string>
|
||||
<string name="label_experimental_settings_desc_summary">这些功能目前尚未完成或者缺少用户经验性/安全性的研究结果。所以,不保证他们是安全的,请不要报告遇到的问题!</string>
|
||||
<string name="label_experimental_settings_keybase_title">Keybase.io 证明</string>
|
||||
<string name="label_experimental_settings_keybase_summary">每次展示密钥时自动从keybase.io获取证明并显示它们</string>
|
||||
<string name="label_experimental_settings_theme_summary">(图标和某些界面还未根据深色主题进行调整)</string>
|
||||
<string name="label_settings_analytics_title">允许使用匿名统计数据</string>
|
||||
<string name="label_settings_analytics_summary">如果开启,发送匿名的统计数据来帮助我们改进此软件</string>
|
||||
<!--Proxy Preferences-->
|
||||
<string name="pref_proxy_tor_title">启用 Tor 代理</string>
|
||||
<string name="pref_proxy_tor_summary">必需已安装 Orbot</string>
|
||||
|
@ -762,8 +759,6 @@
|
|||
<string name="my_keys">我的密钥</string>
|
||||
<string name="nav_backup">备份/恢复</string>
|
||||
<string name="nav_transfer">安全 Wifi 传输</string>
|
||||
<string name="nav_shop">商店</string>
|
||||
<string name="nav_autocrypt">尝试在 Thunderbird 中自动加密</string>
|
||||
<!--hints-->
|
||||
<string name="encrypt_content_edit_text_hint">此处输入要加密的内容</string>
|
||||
<!--certs-->
|
||||
|
@ -1772,12 +1767,6 @@
|
|||
<string name="keylist_item_key_id">密钥 ID:%s</string>
|
||||
<string name="keylist_header_anonymous">匿名</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
<string name="dialog_analytics_consent">为改善所有用户的使用体验,允许OpenKeychain匿名搜集一些使用统计信息吗?\n\n想知道更多,查阅我们的<a href="https://openkeychain.org/help/privacy-policy">隐私政策</a></string>
|
||||
<string name="button_analytics_yes">是的,我愿意帮忙!</string>
|
||||
<string name="button_analytics_no">不用了,谢谢</string>
|
||||
<string name="snack_analytics_accept">感谢帮助!你可以在设置中更改这些偏好。</string>
|
||||
<string name="snack_analytics_reject">没问题,我们不会再次打扰你。如果改变了想法,你可以在设置中更改。</string>
|
||||
<string name="snackbutton_analytics_settings">设置</string>
|
||||
<string name="subkey_action_create">将创建子密钥</string>
|
||||
<string name="subkey_action_revoke">将吊销子密钥</string>
|
||||
<string name="subkey_action_strip">将剥离子密钥</string>
|
||||
|
@ -1785,6 +1774,4 @@
|
|||
<string name="subkey_action_expiry_date">过期时间将更改为%s</string>
|
||||
<string name="share_key_clipboard">通过剪贴板共享</string>
|
||||
<string name="share_key">共享密钥</string>
|
||||
<string name="shop_loading_label">加载商店</string>
|
||||
<string name="shop_title">COTECH 安全密钥</string>
|
||||
</resources>
|
||||
|
|
|
@ -220,20 +220,12 @@
|
|||
<string name="label_sync_settings_keyserver_summary_on">"Every three days, keys are updated from the preferred keyserver"</string>
|
||||
<string name="label_sync_settings_keyserver_summary_off">"Keys are not automatically updated"</string>
|
||||
<string name="label_sync_settings_wifi_title">"Sync only on Wi-Fi"</string>
|
||||
<string name="label_sync_settings_contacts_title">"Link keys to contacts"</string>
|
||||
<string name="label_sync_settings_contacts_summary_on">"Link keys to contacts based on names and email addresses. This happens completely offline on your device."</string>
|
||||
<string name="label_sync_settings_contacts_summary_off">"New keys will not be linked to contacts"</string>
|
||||
<!-- label shown in Android settings under the OpenKeychain account -->
|
||||
<string name="keyserver_sync_settings_title">"Automatic key updates"</string>
|
||||
|
||||
<string name="label_experimental_settings_desc_title">"Warning"</string>
|
||||
<string name="label_experimental_settings_desc_summary">"These features are not yet finished or results of user experience/security research. Thus, don't rely on their security and please don't report issues you encounter!"</string>
|
||||
|
||||
<string name="label_experimental_settings_theme_summary">"(The icons and many screens are not yet adjusted accordingly for the dark theme)"</string>
|
||||
|
||||
<string name="label_settings_analytics_title">Allow anonymous usage statistics</string>
|
||||
<string name="label_settings_analytics_summary">If enabled, sends anonymous usage statistics to help improve the app</string>
|
||||
|
||||
<!-- Proxy Preferences -->
|
||||
<string name="pref_proxy_tor_title">"Enable Tor"</string>
|
||||
<string name="pref_proxy_tor_summary">"Requires Orbot to be installed"</string>
|
||||
|
@ -820,8 +812,6 @@
|
|||
<string name="my_keys">"My Keys"</string>
|
||||
<string name="nav_backup">"Backup/Restore"</string>
|
||||
<string name="nav_transfer">"Secure Wifi Transfer"</string>
|
||||
<string name="nav_shop">Shop</string>
|
||||
<string name="nav_autocrypt">Try Autocrypt with Thunderbird</string>
|
||||
|
||||
<!-- hints -->
|
||||
<string name="encrypt_content_edit_text_hint">"Type text"</string>
|
||||
|
@ -1908,13 +1898,6 @@
|
|||
<string name="keylist_header_anonymous">Anonymous</string>
|
||||
<string name="keylist_header_special">#</string>
|
||||
|
||||
<string name="dialog_analytics_consent">"To improve the experience for all users, may OpenKeychain collect anonymous usage statistics?\n\nTo find out more, see our <a href="https://openkeychain.org/help/privacy-policy">Privacy Policy</a>."</string>
|
||||
<string name="button_analytics_yes">"Yes, I want to help!"</string>
|
||||
<string name="button_analytics_no">"No, thanks"</string>
|
||||
|
||||
<string name="snack_analytics_accept">"Thanks for helping out! You can change this preference in the settings."</string>
|
||||
<string name="snack_analytics_reject">"That's alright, we won't ask again. You can change your mind in the settings."</string>
|
||||
<string name="snackbutton_analytics_settings">"Settings"</string>
|
||||
<string name="subkey_action_create">Subkey will be created</string>
|
||||
<string name="subkey_action_revoke">Subkey will be revoked</string>
|
||||
<string name="subkey_action_strip">Subkey will be stripped</string>
|
||||
|
@ -1923,6 +1906,4 @@
|
|||
<string name="share_key_clipboard">Share key via clipboard</string>
|
||||
<string name="share_key">Share key</string>
|
||||
|
||||
<string name="shop_loading_label">Loading shop…</string>
|
||||
<string name="shop_title">COTECH Security Keys</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountPreferences="@xml/account_preferences"
|
||||
android:accountType="@string/account_type"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name" />
|
|
@ -5,13 +5,6 @@
|
|||
android:summary="@string/label_experimental_settings_desc_summary"
|
||||
android:title="@string/label_experimental_settings_desc_title" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="analytics_consent"
|
||||
android:persistent="true"
|
||||
android:summary="@string/label_settings_analytics_summary"
|
||||
android:title="@string/label_settings_analytics_title" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="light"
|
||||
android:dialogTitle="@string/label_theme"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentAuthority="com.android.contacts"
|
||||
android:accountType="@string/account_type"
|
||||
android:supportsUploading="false"
|
||||
android:userVisible="true"
|
||||
android:allowParallelSyncs="false"
|
||||
android:isAlwaysSyncable="true" />
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<ContactsDataKind
|
||||
android:mimeType="vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key"
|
||||
android:detailColumn="data1" />
|
||||
</ContactsSource>
|
|
@ -3,15 +3,14 @@
|
|||
android:key="syncKeyserver"
|
||||
android:defaultValue="true"
|
||||
android:persistent="true"
|
||||
android:title="@string/label_sync_settings_keyserver_title"/>
|
||||
android:title="@string/label_sync_settings_keyserver_title"
|
||||
android:summaryOn="@string/label_sync_settings_keyserver_summary_on"
|
||||
android:summaryOff="@string/label_sync_settings_keyserver_summary_off"
|
||||
/>
|
||||
<SwitchPreference
|
||||
android:key="enableWifiSyncOnly"
|
||||
android:defaultValue="true"
|
||||
android:persistent="true"
|
||||
android:dependency="syncKeyserver"
|
||||
android:title="@string/label_sync_settings_wifi_title"/>
|
||||
<SwitchPreference
|
||||
android:key="syncContacts"
|
||||
android:persistent="false"
|
||||
android:title="@string/label_sync_settings_contacts_title" />
|
||||
</PreferenceScreen>
|
||||
|
|
Loading…
Add table
Reference in a new issue