token-import: first iteration
This commit is contained in:
parent
21bc6f12f0
commit
af7d36c038
35 changed files with 1098 additions and 77 deletions
|
@ -76,6 +76,7 @@ public class CryptoInputParcelCacheService extends Service {
|
|||
data.setExtrasClassLoader(CryptoInputParcelCacheService.class.getClassLoader());
|
||||
|
||||
// And write out the UUID most and least significant bits.
|
||||
data.setExtrasClassLoader(CryptoInputParcelCacheService.class.getClassLoader());
|
||||
data.putExtra(OpenPgpApi.EXTRA_CALL_UUID1, mTicket.getMostSignificantBits());
|
||||
data.putExtra(OpenPgpApi.EXTRA_CALL_UUID2, mTicket.getLeastSignificantBits());
|
||||
}
|
||||
|
|
|
@ -587,6 +587,11 @@ public class SecurityTokenHelper {
|
|||
return getData(0x00, 0x4F);
|
||||
}
|
||||
|
||||
public String getUrl() throws IOException {
|
||||
byte[] data = getData(0x5F, 0x50);
|
||||
return new String(data);
|
||||
}
|
||||
|
||||
public String getUserId() throws IOException {
|
||||
return getHolderName(getData(0x00, 0x65));
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package org.sufficientlysecure.keychain.service;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
|
|||
byte[] mScannedFingerprints;
|
||||
byte[] mSecurityTokenAid;
|
||||
String mSecurityTokenUserId;
|
||||
private String mSecurityTokenUrl;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -162,6 +163,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
|
|||
mScannedFingerprints = mSecurityTokenHelper.getFingerprints();
|
||||
mSecurityTokenAid = mSecurityTokenHelper.getAid();
|
||||
mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
|
||||
mSecurityTokenUrl = mSecurityTokenHelper.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -194,8 +196,8 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
|
|||
finish();
|
||||
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
|
||||
mScannedFingerprints, mSecurityTokenAid, mSecurityTokenUserId);
|
||||
Fragment frag = CreateSecurityTokenImportFragment.newInstance(
|
||||
mScannedFingerprints, mSecurityTokenAid, mSecurityTokenUserId, mSecurityTokenUrl);
|
||||
loadFragment(frag, FragAction.TO_RIGHT);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* 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.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.CreateSecurityTokenImportPresenter.CreateSecurityTokenImportMvpView;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.AbstractCallback;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.widget.StatusIndicator;
|
||||
import org.sufficientlysecure.keychain.ui.widget.StatusIndicator.Status;
|
||||
import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator;
|
||||
|
||||
|
||||
public class CreateSecurityTokenImportFragment extends Fragment implements CreateSecurityTokenImportMvpView,
|
||||
OnClickListener {
|
||||
private static final String ARG_FINGERPRINTS = "fingerprint";
|
||||
private static final String ARG_AID = "aid";
|
||||
private static final String ARG_USER_ID = "user_ids";
|
||||
private static final String ARG_URL = "key_uri";
|
||||
|
||||
CreateSecurityTokenImportPresenter presenter;
|
||||
private ViewGroup statusLayoutGroup;
|
||||
private ToolableViewAnimator actionAnimator;
|
||||
|
||||
ImportKeyringParcel currentImportKeyringParcel;
|
||||
PromoteKeyringParcel currentPromoteKeyringParcel;
|
||||
private LayoutInflater layoutInflater;
|
||||
private StatusIndicator latestStatusIndicator;
|
||||
|
||||
public static Fragment newInstanceForDebug() {
|
||||
// byte[] scannedFps = KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E");
|
||||
byte[] scannedFps = KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a");
|
||||
return newInstance(scannedFps, Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu3.asc");
|
||||
}
|
||||
|
||||
public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId, String tokenUrl) {
|
||||
CreateSecurityTokenImportFragment frag = new CreateSecurityTokenImportFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putByteArray(ARG_FINGERPRINTS, scannedFingerprints);
|
||||
args.putByteArray(ARG_AID, nfcAid);
|
||||
args.putString(ARG_USER_ID, userId);
|
||||
args.putString(ARG_URL, tokenUrl);
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle args = getArguments();
|
||||
|
||||
byte[] tokenFingerprints = args.getByteArray(ARG_FINGERPRINTS);
|
||||
byte[] tokenAid = args.getByteArray(ARG_AID);
|
||||
String tokenUserId = args.getString(ARG_USER_ID);
|
||||
String tokenUrl = args.getString(ARG_URL);
|
||||
|
||||
presenter = new CreateSecurityTokenImportPresenter(
|
||||
getContext(), tokenFingerprints, tokenAid, tokenUserId, tokenUrl, getLoaderManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
this.layoutInflater = inflater;
|
||||
View view = inflater.inflate(R.layout.create_security_token_import_fragment, container, false);
|
||||
|
||||
statusLayoutGroup = (ViewGroup) view.findViewById(R.id.status_indicator_layout);
|
||||
actionAnimator = (ToolableViewAnimator) view.findViewById(R.id.action_animator);
|
||||
|
||||
view.findViewById(R.id.button_import).setOnClickListener(this);
|
||||
view.findViewById(R.id.button_view_key).setOnClickListener(this);
|
||||
view.findViewById(R.id.button_retry).setOnClickListener(this);
|
||||
view.findViewById(R.id.button_reset_token_1).setOnClickListener(this);
|
||||
view.findViewById(R.id.button_reset_token_2).setOnClickListener(this);
|
||||
view.findViewById(R.id.button_reset_token_3).setOnClickListener(this);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
presenter.setView(this);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.token_setup, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
presenter.onActivityCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishAndShowKey(long masterKeyId) {
|
||||
Activity activity = getActivity();
|
||||
|
||||
Intent viewKeyIntent = new Intent(activity, ViewKeyActivity.class);
|
||||
// use the imported masterKeyId, not the one from the token, because
|
||||
// that one might* just have been a subkey of the imported key
|
||||
viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
|
||||
|
||||
if (activity instanceof CreateKeyActivity) {
|
||||
((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent);
|
||||
} else {
|
||||
activity.startActivity(viewKeyIntent);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusLineAdd(StatusLine statusLine) {
|
||||
if (latestStatusIndicator != null) {
|
||||
throw new IllegalStateException("Cannot set next status line before completing previous!");
|
||||
}
|
||||
|
||||
View line = layoutInflater.inflate(R.layout.status_indicator_line, statusLayoutGroup, false);
|
||||
|
||||
latestStatusIndicator = (StatusIndicator) line.findViewById(R.id.status_indicator);
|
||||
latestStatusIndicator.setDisplayedChild(Status.PROGRESS);
|
||||
TextView latestStatusText = (TextView) line.findViewById(R.id.status_text);
|
||||
latestStatusText.setText(statusLine.stringRes);
|
||||
|
||||
statusLayoutGroup.addView(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusLineOk() {
|
||||
latestStatusIndicator.setDisplayedChild(Status.OK);
|
||||
latestStatusIndicator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusLineError() {
|
||||
latestStatusIndicator.setDisplayedChild(Status.ERROR);
|
||||
latestStatusIndicator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetStatusLines() {
|
||||
latestStatusIndicator = null;
|
||||
statusLayoutGroup.removeAllViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showActionImport() {
|
||||
actionAnimator.setDisplayedChildId(R.id.token_layout_import);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showActionViewKey() {
|
||||
actionAnimator.setDisplayedChildId(R.id.token_layout_ok);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showActionRetryOrFromFile() {
|
||||
actionAnimator.setDisplayedChildId(R.id.token_layout_not_found);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideAction() {
|
||||
actionAnimator.setDisplayedChild(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationImportKey(byte[] importKeyData) {
|
||||
if (currentImportKeyringParcel != null) {
|
||||
throw new IllegalStateException("Cannot trigger import operation twice!");
|
||||
}
|
||||
|
||||
currentImportKeyringParcel =
|
||||
ImportKeyringParcel.createImportKeyringParcel(ParcelableKeyRing.createFromEncodedBytes(importKeyData));
|
||||
cryptoImportOperationHelper.setOperationMinimumDelay(1000L);
|
||||
cryptoImportOperationHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationPromote(long masterKeyId, byte[] cardAid) {
|
||||
if (currentImportKeyringParcel != null) {
|
||||
throw new IllegalStateException("Cannot trigger import operation twice!");
|
||||
}
|
||||
|
||||
currentPromoteKeyringParcel = PromoteKeyringParcel.createPromoteKeyringParcel(masterKeyId, cardAid, null);
|
||||
cryptoPromoteOperationHelper.setOperationMinimumDelay(1000L);
|
||||
cryptoPromoteOperationHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.button_import: {
|
||||
presenter.onClickImport();
|
||||
break;
|
||||
}
|
||||
case R.id.button_retry: {
|
||||
presenter.onClickRetry();
|
||||
break;
|
||||
}
|
||||
case R.id.button_view_key: {
|
||||
presenter.onClickViewKey();
|
||||
break;
|
||||
}
|
||||
case R.id.button_reset_token_1:
|
||||
case R.id.button_reset_token_2:
|
||||
case R.id.button_reset_token_3: {
|
||||
presenter.onClickResetToken();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> cryptoImportOperationHelper =
|
||||
new CryptoOperationHelper<>(0, this, new AbstractCallback<ImportKeyringParcel, ImportKeyResult>() {
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return currentImportKeyringParcel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
currentImportKeyringParcel = null;
|
||||
presenter.onImportSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
currentImportKeyringParcel = null;
|
||||
presenter.onImportError();
|
||||
}
|
||||
}, null);
|
||||
|
||||
CryptoOperationHelper<PromoteKeyringParcel, PromoteKeyResult> cryptoPromoteOperationHelper =
|
||||
new CryptoOperationHelper<>(1, this, new AbstractCallback<PromoteKeyringParcel, PromoteKeyResult>() {
|
||||
@Override
|
||||
public PromoteKeyringParcel createOperationInput() {
|
||||
return currentPromoteKeyringParcel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(PromoteKeyResult result) {
|
||||
currentPromoteKeyringParcel = null;
|
||||
presenter.onPromoteSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(PromoteKeyResult result) {
|
||||
currentPromoteKeyringParcel = null;
|
||||
presenter.onPromoteError();
|
||||
}
|
||||
}, null);
|
||||
|
||||
enum StatusLine {
|
||||
SEARCH_LOCAL (R.string.status_search_local),
|
||||
SEARCH_URI (R.string.status_search_uri),
|
||||
SEARCH_KEYSERVER (R.string.status_search_keyserver),
|
||||
IMPORT (R.string.status_import),
|
||||
TOKEN_PROMOTE(R.string.status_token_promote),
|
||||
TOKEN_CHECK (R.string.status_token_check);
|
||||
|
||||
@StringRes
|
||||
private int stringRes;
|
||||
|
||||
StatusLine(@StringRes int stringRes) {
|
||||
this.stringRes = stringRes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* 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.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.ui.CreateSecurityTokenImportFragment.StatusLine;
|
||||
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.KeyRetrievalResult;
|
||||
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.KeyserverRetrievalLoader;
|
||||
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.LocalKeyLookupLoader;
|
||||
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.UriKeyRetrievalLoader;
|
||||
|
||||
|
||||
class CreateSecurityTokenImportPresenter {
|
||||
private static final int LOADER_LOCAL = 0;
|
||||
private static final int LOADER_URI = 1;
|
||||
private static final int LOADER_KEYSERVER = 2;
|
||||
|
||||
private Context context;
|
||||
|
||||
private byte[][] tokenFingerprints;
|
||||
private byte[] tokenAid;
|
||||
private double tokenVersion;
|
||||
private String tokenUserId;
|
||||
private final String tokenUrl;
|
||||
|
||||
private LoaderManager loaderManager;
|
||||
private CreateSecurityTokenImportMvpView view;
|
||||
private boolean searchedLocally;
|
||||
private boolean searchedAtUri;
|
||||
private boolean searchedKeyservers;
|
||||
|
||||
private byte[] importKeyData;
|
||||
private Long masterKeyId;
|
||||
|
||||
|
||||
public CreateSecurityTokenImportPresenter(Context context, byte[] tokenFingerprints, byte[] tokenAid,
|
||||
String tokenUserId, String tokenUrl, LoaderManager loaderManager) {
|
||||
this.context = context.getApplicationContext();
|
||||
|
||||
this.tokenAid = tokenAid;
|
||||
this.tokenUserId = tokenUserId;
|
||||
this.tokenUrl = tokenUrl;
|
||||
this.loaderManager = loaderManager;
|
||||
|
||||
if (tokenFingerprints.length % 20 != 0) {
|
||||
throw new IllegalArgumentException("fingerprints must be multiple of 20 bytes!");
|
||||
}
|
||||
this.tokenFingerprints = new byte[tokenFingerprints.length / 20][];
|
||||
for (int i = 0; i < tokenFingerprints.length / 20; i++) {
|
||||
this.tokenFingerprints[i] = new byte[20];
|
||||
System.arraycopy(tokenFingerprints, i*20, this.tokenFingerprints[i], 0, 20);
|
||||
}
|
||||
}
|
||||
|
||||
public void setView(CreateSecurityTokenImportMvpView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void onActivityCreated() {
|
||||
continueSearch();
|
||||
}
|
||||
|
||||
private void continueSearchAfterError() {
|
||||
view.statusLineError();
|
||||
continueSearch();
|
||||
}
|
||||
|
||||
private void continueSearch() {
|
||||
if (!searchedLocally) {
|
||||
view.statusLineAdd(StatusLine.SEARCH_LOCAL);
|
||||
loaderManager.restartLoader(LOADER_LOCAL, null, loaderCallbacks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!searchedAtUri) {
|
||||
view.statusLineAdd(StatusLine.SEARCH_URI);
|
||||
loaderManager.restartLoader(LOADER_URI, null, loaderCallbacks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!searchedKeyservers) {
|
||||
view.statusLineAdd(StatusLine.SEARCH_KEYSERVER);
|
||||
loaderManager.restartLoader(LOADER_KEYSERVER, null, loaderCallbacks);
|
||||
return;
|
||||
}
|
||||
|
||||
view.showActionRetryOrFromFile();
|
||||
}
|
||||
|
||||
private LoaderCallbacks<KeyRetrievalResult> loaderCallbacks = new LoaderCallbacks<KeyRetrievalResult>() {
|
||||
@Override
|
||||
public Loader<KeyRetrievalResult> onCreateLoader(int id, Bundle args) {
|
||||
switch (id) {
|
||||
case LOADER_LOCAL:
|
||||
return new LocalKeyLookupLoader(context, tokenFingerprints);
|
||||
case LOADER_URI:
|
||||
return new UriKeyRetrievalLoader(context, tokenUrl, tokenFingerprints);
|
||||
case LOADER_KEYSERVER:
|
||||
return new KeyserverRetrievalLoader(context, tokenFingerprints[0]);
|
||||
}
|
||||
throw new IllegalArgumentException("called with unknown loader id!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<KeyRetrievalResult> loader, KeyRetrievalResult data) {
|
||||
switch (loader.getId()) {
|
||||
case LOADER_LOCAL: {
|
||||
searchedLocally = true;
|
||||
break;
|
||||
}
|
||||
case LOADER_URI: {
|
||||
searchedAtUri = true;
|
||||
break;
|
||||
}
|
||||
case LOADER_KEYSERVER: {
|
||||
searchedKeyservers = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("called with unknown loader id!");
|
||||
}
|
||||
}
|
||||
|
||||
if (data.isSuccess()) {
|
||||
processResult(data);
|
||||
} else {
|
||||
continueSearchAfterError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<KeyRetrievalResult> loader) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private void processResult(KeyRetrievalResult result) {
|
||||
view.statusLineOk();
|
||||
|
||||
byte[] importKeyData = result.getKeyData();
|
||||
Long masterKeyId = result.getMasterKeyId();
|
||||
if (importKeyData != null && masterKeyId != null) {
|
||||
view.showActionImport();
|
||||
this.importKeyData = importKeyData;
|
||||
this.masterKeyId = masterKeyId;
|
||||
return;
|
||||
}
|
||||
|
||||
if (masterKeyId != null) {
|
||||
this.masterKeyId = masterKeyId;
|
||||
view.statusLineAdd(StatusLine.TOKEN_CHECK);
|
||||
view.operationPromote(masterKeyId, tokenAid);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Method can only be called with successful result!");
|
||||
}
|
||||
|
||||
void onClickImport() {
|
||||
view.statusLineAdd(StatusLine.IMPORT);
|
||||
view.hideAction();
|
||||
view.operationImportKey(importKeyData);
|
||||
}
|
||||
|
||||
void onImportSuccess() {
|
||||
view.statusLineOk();
|
||||
view.statusLineAdd(StatusLine.TOKEN_PROMOTE);
|
||||
view.operationPromote(masterKeyId, tokenAid);
|
||||
}
|
||||
|
||||
void onImportError() {
|
||||
view.statusLineError();
|
||||
}
|
||||
|
||||
void onPromoteSuccess() {
|
||||
view.statusLineOk();
|
||||
view.showActionViewKey();
|
||||
}
|
||||
|
||||
void onPromoteError() {
|
||||
view.statusLineError();
|
||||
}
|
||||
|
||||
void onClickRetry() {
|
||||
searchedLocally = false;
|
||||
searchedAtUri = false;
|
||||
searchedKeyservers = false;
|
||||
|
||||
view.hideAction();
|
||||
view.resetStatusLines();
|
||||
continueSearch();
|
||||
}
|
||||
|
||||
void onClickViewKey() {
|
||||
view.finishAndShowKey(masterKeyId);
|
||||
}
|
||||
|
||||
public void onClickResetToken() {
|
||||
|
||||
}
|
||||
|
||||
interface CreateSecurityTokenImportMvpView {
|
||||
void statusLineAdd(StatusLine statusLine);
|
||||
void statusLineOk();
|
||||
void statusLineError();
|
||||
void resetStatusLines();
|
||||
|
||||
void showActionImport();
|
||||
void showActionViewKey();
|
||||
void showActionRetryOrFromFile();
|
||||
void hideAction();
|
||||
|
||||
void operationImportKey(byte[] importKeyData);
|
||||
void operationPromote(long masterKeyId, byte[] cardAid);
|
||||
|
||||
void finishAndShowKey(long masterKeyId);
|
||||
}
|
||||
}
|
|
@ -44,7 +44,6 @@ import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListene
|
|||
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -72,7 +71,6 @@ public class CreateSecurityTokenImportResetFragment
|
|||
private TextView vUserId;
|
||||
private TextView mNextButton;
|
||||
private RadioButton mRadioImport;
|
||||
private RadioButton mRadioFile;
|
||||
private RadioButton mRadioReset;
|
||||
private View mResetWarning;
|
||||
|
||||
|
@ -117,7 +115,6 @@ public class CreateSecurityTokenImportResetFragment
|
|||
vUserId = (TextView) view.findViewById(R.id.token_userid);
|
||||
mNextButton = (TextView) view.findViewById(R.id.create_key_next_button);
|
||||
mRadioImport = (RadioButton) view.findViewById(R.id.token_decision_import);
|
||||
mRadioFile = (RadioButton) view.findViewById(R.id.token_decision_file);
|
||||
mRadioReset = (RadioButton) view.findViewById(R.id.token_decision_reset);
|
||||
mResetWarning = view.findViewById(R.id.token_import_reset_warning);
|
||||
|
||||
|
@ -141,8 +138,6 @@ public class CreateSecurityTokenImportResetFragment
|
|||
resetCard();
|
||||
} else if (mRadioImport.isChecked()){
|
||||
importKey();
|
||||
} else {
|
||||
importFile();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -158,17 +153,6 @@ public class CreateSecurityTokenImportResetFragment
|
|||
}
|
||||
}
|
||||
});
|
||||
mRadioFile.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (isChecked) {
|
||||
mNextButton.setText(R.string.key_list_fab_import);
|
||||
mNextButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_folder_grey_24dp, 0);
|
||||
mNextButton.setVisibility(View.VISIBLE);
|
||||
mResetWarning.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
mRadioReset.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
|
@ -183,7 +167,6 @@ public class CreateSecurityTokenImportResetFragment
|
|||
|
||||
setData();
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -214,21 +197,9 @@ public class CreateSecurityTokenImportResetFragment
|
|||
}
|
||||
|
||||
public void importKey() {
|
||||
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
|
||||
keyList.add(ParcelableKeyRing.createFromReference(mTokenFingerprint, null, null, null));
|
||||
mKeyList = keyList;
|
||||
|
||||
mKeyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
|
||||
|
||||
super.setProgressMessageResource(R.string.progress_importing);
|
||||
|
||||
super.cryptoOperation();
|
||||
}
|
||||
|
||||
public void importFile() {
|
||||
Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
|
||||
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
|
||||
startActivity(intent);
|
||||
// Fragment frag = CreateSecurityTokenImportFragment.newInstance(mTokenFingerprints, mTokenAid, mTokenUserId,
|
||||
// keyUdi);
|
||||
// mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
|
||||
}
|
||||
|
||||
public void resetCard() {
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -26,9 +27,12 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
||||
|
||||
public class CreateSecurityTokenWaitFragment extends Fragment {
|
||||
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Vincent Breitmoser <look@my.amazin.horse>
|
||||
*
|
||||
* 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 java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import okhttp3.Request.Builder;
|
||||
import okhttp3.Response;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverClient;
|
||||
import org.sufficientlysecure.keychain.keyimport.KeyserverClient.QueryFailedException;
|
||||
import org.sufficientlysecure.keychain.network.OkHttpClientFactory;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.KeyRetrievalResult;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
|
||||
public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrievalResult> {
|
||||
private static final long MIN_OPERATION_TIME_MILLIS = 1500;
|
||||
|
||||
|
||||
private KeyRetrievalResult cachedResult;
|
||||
|
||||
|
||||
public PublicKeyRetrievalLoader(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyRetrievalResult onLoadInBackground() {
|
||||
long startTime = SystemClock.elapsedRealtime();
|
||||
|
||||
KeyRetrievalResult keyRetrievalResult = super.onLoadInBackground();
|
||||
|
||||
try {
|
||||
long elapsedTime = startTime - SystemClock.elapsedRealtime();
|
||||
if (elapsedTime < MIN_OPERATION_TIME_MILLIS) {
|
||||
Thread.sleep(MIN_OPERATION_TIME_MILLIS - elapsedTime);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// nvm
|
||||
}
|
||||
|
||||
return keyRetrievalResult;
|
||||
}
|
||||
|
||||
public static class LocalKeyLookupLoader extends PublicKeyRetrievalLoader {
|
||||
private final KeyRepository keyRepository;
|
||||
private final byte[][] fingerprints;
|
||||
|
||||
public LocalKeyLookupLoader(Context context, byte[][] fingerprints) {
|
||||
super(context);
|
||||
|
||||
this.fingerprints = fingerprints;
|
||||
this.keyRepository = KeyRepository.createDatabaseInteractor(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRetrievalResult loadInBackground() {
|
||||
try {
|
||||
// TODO check other fingerprints
|
||||
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(fingerprints[0]);
|
||||
CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(masterKeyId);
|
||||
switch (cachedPublicKeyRing.getSecretKeyType(masterKeyId)) {
|
||||
case PASSPHRASE:
|
||||
case PASSPHRASE_EMPTY: {
|
||||
return KeyRetrievalResult.createWithMasterKeyIdAndSecretAvailable(masterKeyId);
|
||||
}
|
||||
|
||||
case GNU_DUMMY:
|
||||
case DIVERT_TO_CARD:
|
||||
case UNAVAILABLE: {
|
||||
return KeyRetrievalResult.createWithMasterKeyId(masterKeyId);
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new IllegalStateException("Unhandled SecretKeyType!");
|
||||
}
|
||||
}
|
||||
} catch (NotFoundException e) {
|
||||
return KeyRetrievalResult.createWithError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UriKeyRetrievalLoader extends PublicKeyRetrievalLoader {
|
||||
byte[][] fingerprints;
|
||||
String yubikeyUri;
|
||||
|
||||
public UriKeyRetrievalLoader(Context context, String yubikeyUri, byte[][] fingerprints) {
|
||||
super(context);
|
||||
|
||||
this.yubikeyUri = yubikeyUri;
|
||||
this.fingerprints = fingerprints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRetrievalResult loadInBackground() {
|
||||
try {
|
||||
Response execute =
|
||||
OkHttpClientFactory.getSimpleClient().newCall(new Builder().url(yubikeyUri).build()).execute();
|
||||
if (execute.isSuccessful()) {
|
||||
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(execute.body().bytes());
|
||||
if (Arrays.equals(fingerprints[0], keyRing.getFingerprint())) {
|
||||
return KeyRetrievalResult.createWithKeyringdata(keyRing.getMasterKeyId(), keyRing.getEncoded());
|
||||
}
|
||||
}
|
||||
} catch (IOException | PgpGeneralException e) {
|
||||
Log.e(Constants.TAG, "error retrieving key from uri", e);
|
||||
}
|
||||
|
||||
return KeyRetrievalResult.createWithError();
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeyserverRetrievalLoader extends PublicKeyRetrievalLoader {
|
||||
byte[] fingerprint;
|
||||
|
||||
public KeyserverRetrievalLoader(Context context, byte[] fingerprint) {
|
||||
super(context);
|
||||
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRetrievalResult loadInBackground() {
|
||||
HkpKeyserverAddress preferredKeyserver = Preferences.getPreferences(getContext()).getPreferredKeyserver();
|
||||
ParcelableProxy parcelableProxy = Preferences.getPreferences(getContext()).getParcelableProxy();
|
||||
|
||||
HkpKeyserverClient keyserverClient = HkpKeyserverClient.fromHkpKeyserverAddress(preferredKeyserver);
|
||||
|
||||
try {
|
||||
String keyString =
|
||||
keyserverClient.get("0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint), parcelableProxy);
|
||||
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(keyString.getBytes());
|
||||
|
||||
return KeyRetrievalResult.createWithKeyringdata(keyRing.getMasterKeyId(), keyRing.getEncoded());
|
||||
} catch (QueryFailedException | IOException | PgpGeneralException e) {
|
||||
Log.e(Constants.TAG, "error retrieving key from keyserver", e);
|
||||
}
|
||||
|
||||
return KeyRetrievalResult.createWithError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliverResult(KeyRetrievalResult result) {
|
||||
cachedResult = result;
|
||||
|
||||
if (isStarted()) {
|
||||
super.deliverResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
if (cachedResult != null) {
|
||||
deliverResult(cachedResult);
|
||||
}
|
||||
|
||||
if (takeContentChanged() || cachedResult == null) {
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
static abstract class KeyRetrievalResult {
|
||||
@Nullable
|
||||
abstract Long getMasterKeyId();
|
||||
@Nullable
|
||||
abstract byte[] getKeyData();
|
||||
abstract boolean isSecretKeyAvailable();
|
||||
|
||||
boolean isSuccess() {
|
||||
return getMasterKeyId() != null || getKeyData() != null;
|
||||
}
|
||||
|
||||
static KeyRetrievalResult createWithError() {
|
||||
return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(null, null, false);
|
||||
}
|
||||
|
||||
static KeyRetrievalResult createWithKeyringdata(long masterKeyId, byte[] keyringData) {
|
||||
return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(masterKeyId, keyringData, false);
|
||||
}
|
||||
|
||||
static KeyRetrievalResult createWithMasterKeyIdAndSecretAvailable(long masterKeyId) {
|
||||
return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(masterKeyId, null, true);
|
||||
}
|
||||
|
||||
static KeyRetrievalResult createWithMasterKeyId(long masterKeyId) {
|
||||
return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(masterKeyId, null, false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -99,7 +99,6 @@ public class ViewKeySecurityTokenFragment
|
|||
mMasterKeyId = args.getLong(ARG_MASTER_KEY_ID);
|
||||
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,9 +25,11 @@ import android.app.Activity;
|
|||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
@ -55,6 +57,8 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||
*/
|
||||
public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
|
||||
|
||||
private long operationStartTime;
|
||||
|
||||
public interface Callback<T extends Parcelable, S extends OperationResult> {
|
||||
T createOperationInput();
|
||||
|
||||
|
@ -67,6 +71,19 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
|||
boolean onCryptoSetProgress(String msg, int progress, int max);
|
||||
}
|
||||
|
||||
public static abstract class AbstractCallback<T extends Parcelable, S extends OperationResult>
|
||||
implements Callback<T,S> {
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
throw new UnsupportedOperationException("Unexpectedly cancelled operation!!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// request codes from CryptoOperationHelper are created essentially
|
||||
// a static property, used to identify requestCodes meant for this
|
||||
// particular helper. a request code looks as follows:
|
||||
|
@ -85,6 +102,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
|||
|
||||
private Integer mProgressMessageResource;
|
||||
private boolean mCancellable = false;
|
||||
private Long minimumOperationDelay;
|
||||
|
||||
private FragmentActivity mActivity;
|
||||
private Fragment mFragment;
|
||||
|
@ -119,6 +137,10 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
|||
mProgressMessageResource = id;
|
||||
}
|
||||
|
||||
public void setOperationMinimumDelay(Long delay) {
|
||||
this.minimumOperationDelay = delay;
|
||||
}
|
||||
|
||||
public void setProgressCancellable(boolean cancellable) {
|
||||
mCancellable = cancellable;
|
||||
}
|
||||
|
@ -323,10 +345,11 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
|||
}
|
||||
|
||||
public void cryptoOperation() {
|
||||
operationStartTime = SystemClock.elapsedRealtime();
|
||||
cryptoOperation(CryptoInputParcel.createCryptoInputParcel(new Date()));
|
||||
}
|
||||
|
||||
public void onHandleResult(OperationResult result) {
|
||||
private void onHandleResult(final OperationResult result) {
|
||||
Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
|
||||
|
||||
if (result instanceof InputPendingResult) {
|
||||
|
@ -340,6 +363,21 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
|||
|
||||
dismissProgress();
|
||||
|
||||
long elapsedTime = SystemClock.elapsedRealtime() - operationStartTime;
|
||||
if (minimumOperationDelay == null || elapsedTime > minimumOperationDelay) {
|
||||
returnResultToCallback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
returnResultToCallback(result);
|
||||
}
|
||||
}, minimumOperationDelay - elapsedTime);
|
||||
}
|
||||
|
||||
private void returnResultToCallback(OperationResult result) {
|
||||
try {
|
||||
if (result.success()) {
|
||||
// noinspection unchecked, because type erasure :(
|
||||
|
|
|
@ -151,7 +151,8 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
|
|||
|
||||
private Intent getTrustIdActivityIntentIfResolvable(String packageName, String autocryptPeer) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(packageName + ".AUTOCRYPT_PEER_ACTION");
|
||||
intent.setAction("org.autocrypt.PEER_ACTION");
|
||||
intent.setPackage(packageName);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, autocryptPeer);
|
||||
|
||||
|
|
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_bomb_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_bomb_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 616 B |
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_bomb_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_bomb_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 412 B |
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_bomb_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_bomb_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 751 B |
BIN
OpenKeychain/src/main/res/drawable-xxhdpi/ic_bomb_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-xxhdpi/ic_bomb_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
OpenKeychain/src/main/res/drawable-xxxhdpi/ic_bomb_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-xxxhdpi/ic_bomb_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,197 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/yubi_icon" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="Gathering information for Security Token…"
|
||||
style="?android:textAppearanceMedium"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:animateLayoutChanges="true"
|
||||
android:id="@+id/status_indicator_layout">
|
||||
|
||||
<!--
|
||||
<include layout="@layout/status_indicator_line" />
|
||||
-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/action_animator"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:inAnimation="@anim/fade_in_delayed"
|
||||
android:outAnimation="@anim/fade_out"
|
||||
android:clipChildren="false"
|
||||
custom:initialView="01">
|
||||
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/token_layout_not_found">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Key not found!"
|
||||
style="?android:textAppearanceLarge"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_retry"
|
||||
android:drawableLeft="@drawable/ic_repeat_grey_24dp"
|
||||
android:drawableStart="@drawable/ic_repeat_grey_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/card_view_button"
|
||||
android:text="Retry Search"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_load_file"
|
||||
android:drawableLeft="@drawable/ic_folder_grey_24dp"
|
||||
android:drawableStart="@drawable/ic_folder_grey_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/card_view_button"
|
||||
android:text="Load from File"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_reset_token_1"
|
||||
android:drawableLeft="@drawable/ic_bomb_24dp"
|
||||
android:drawableStart="@drawable/ic_bomb_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/android_red_dark"
|
||||
android:text="Reset Security Token"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/token_layout_import">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Key found!"
|
||||
style="?android:textAppearanceLarge"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_import"
|
||||
android:drawableLeft="@drawable/ic_key_plus_grey600_24dp"
|
||||
android:drawableStart="@drawable/ic_key_plus_grey600_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/card_view_button"
|
||||
android:text="Import"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_reset_token_2"
|
||||
android:drawableLeft="@drawable/ic_bomb_24dp"
|
||||
android:drawableStart="@drawable/ic_bomb_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/android_red_dark"
|
||||
android:text="Reset Security Token"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/token_layout_ok">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?listPreferredItemHeight"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Ready for use!"
|
||||
style="?android:textAppearanceLarge"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_view_key"
|
||||
android:drawableLeft="@drawable/ic_vpn_key_grey_24dp"
|
||||
android:drawableStart="@drawable/ic_vpn_key_grey_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/card_view_button"
|
||||
android:text="View Key"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button_reset_token_3"
|
||||
android:drawableLeft="@drawable/ic_bomb_24dp"
|
||||
android:drawableStart="@drawable/ic_bomb_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/android_red_dark"
|
||||
android:text="Reset Security Token"
|
||||
style="?borderlessButtonStyle"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -80,14 +80,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/security_token_import_radio" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/token_decision_file"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/security_token_file_radio" />
|
||||
android:text="@string/security_token_radio_use_existing" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/token_decision_reset"
|
||||
|
@ -95,18 +88,11 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/security_token_reset_radio" />
|
||||
android:text="@string/security_token_radio_reset" />
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/yubikey_decision"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/security_token_import_fragment"
|
||||
android:layout_width="fill_parent"
|
||||
|
|
25
OpenKeychain/src/main/res/layout/status_indicator_line.xml
Normal file
25
OpenKeychain/src/main/res/layout/status_indicator_line.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/status_layout1">
|
||||
|
||||
<org.sufficientlysecure.keychain.ui.widget.StatusIndicator
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:id="@+id/status_indicator"
|
||||
android:layout_margin="4dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:id="@+id/status_text"
|
||||
android:text="Searching in key list…"
|
||||
style="?android:textAppearanceMedium"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
9
OpenKeychain/src/main/res/menu/token_setup.xml
Normal file
9
OpenKeychain/src/main/res/menu/token_setup.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:title="View Log"
|
||||
android:id="@+id/view_log"
|
||||
app:showAsAction="never"
|
||||
/>
|
||||
</menu>
|
|
@ -1458,9 +1458,9 @@
|
|||
<string name="security_token_create">Halte den Security-Token gegen die Rückseite deines Geräts.</string>
|
||||
<string name="security_token_reset_or_import">Dieses Sicherheits-Token enthält bereits einen Schlüssel. Um es zu benutzen, benötigen wir zusätzliche Schlüsselinformationen. Diese Informationen können auf einem Keyserver gesucht oder aus einer Datei importiert werden.</string>
|
||||
<string name="btn_reset">Zurücksetzen</string>
|
||||
<string name="security_token_import_radio">Suche Schlüsselinformation auf Schlüsselserver</string>
|
||||
<string name="security_token_radio_use_existing">Suche Schlüsselinformation auf Schlüsselserver</string>
|
||||
<string name="security_token_file_radio">Importiere Schlüsselinformation von Datei</string>
|
||||
<string name="security_token_reset_radio">Security-Token zurücksetzen</string>
|
||||
<string name="security_token_radio_reset">Security-Token zurücksetzen</string>
|
||||
<string name="security_token_reset_warning">Das Zurücksetzen des Security-Tokens zerstört die darauf gespeicherten Schlüssel vollständig. Mit diesen Schlüssel verschlüsselte Nachrichten/Dateien können danach nicht mehr entschlüsselt werden!</string>
|
||||
<string name="snack_security_token_other">Es ist ein anderer Schlüssel auf dem Security-Token gespeichert!</string>
|
||||
<string name="security_token_error">Fehler: %s</string>
|
||||
|
|
|
@ -1475,9 +1475,9 @@
|
|||
<string name="security_token_create">Sostenga el token de seguridad contra la parte trasera de su dispositivo.</string>
|
||||
<string name="security_token_reset_or_import">Este token de seguridad ya contiene una clave. Para usarla, necesitamos información adicional de la clave. Esta información se puede buscar en un servidor de claves o se puede importar de un fichero.</string>
|
||||
<string name="btn_reset">Reiniciar</string>
|
||||
<string name="security_token_import_radio">Buscar información de la clave en el servidor de claves</string>
|
||||
<string name="security_token_radio_use_existing">Buscar información de la clave en el servidor de claves</string>
|
||||
<string name="security_token_file_radio">Importar información de la clave desde un fichero</string>
|
||||
<string name="security_token_reset_radio">Reinicializar token de seguridad</string>
|
||||
<string name="security_token_radio_reset">Reinicializar token de seguridad</string>
|
||||
<string name="security_token_reset_warning">Reinicializar el token de seguridad destruirá por completo las claves que almacene. ¡Acto seguido no podrá descifrar los mensajes/ficheros cifrados con esta clave!</string>
|
||||
<string name="snack_security_token_other">¡La clave almacenada en el token de seguridad es diferente!</string>
|
||||
<string name="security_token_error">Error: %s</string>
|
||||
|
|
|
@ -1291,7 +1291,7 @@
|
|||
<string name="security_token_serial_no">Serie Zbk: %s</string>
|
||||
<string name="security_token_key_holder_not_set"><![CDATA[Key holder: <not set>]]></string>
|
||||
<string name="btn_reset">Berrezarri</string>
|
||||
<string name="security_token_reset_radio">Berrezarri Segurtasun Lekukoa</string>
|
||||
<string name="security_token_radio_reset">Berrezarri Segurtasun Lekukoa</string>
|
||||
<string name="security_token_error">Akatsa: %s</string>
|
||||
<plurals name="security_token_error_pin">
|
||||
<item quantity="one">PIN okerra!\n%d saiakera gelditzen da.</item>
|
||||
|
|
|
@ -1475,9 +1475,9 @@
|
|||
<string name="security_token_create">Tenir le jeton de sécurité contre le dos de votre appareil.</string>
|
||||
<string name="security_token_reset_or_import">Ce jeton de sécurité contient déjà une clé. Des informations supplémentaires sont requises pour l\'utiliser. Elles peuvent être recherchées sur un serveur de clés ou importer d\'un fichier.</string>
|
||||
<string name="btn_reset">Réinitialiser</string>
|
||||
<string name="security_token_import_radio">Rechercher les informations de la clé sur le serveur de clés</string>
|
||||
<string name="security_token_radio_use_existing">Rechercher les informations de la clé sur le serveur de clés</string>
|
||||
<string name="security_token_file_radio">Importer les informations de la clé d\'un fichier</string>
|
||||
<string name="security_token_reset_radio">Réinitialiser le jeton de sécurité</string>
|
||||
<string name="security_token_radio_reset">Réinitialiser le jeton de sécurité</string>
|
||||
<string name="security_token_reset_warning">La réinitialisation du jeton de sécurité détruit complètement les clés qu\'il contient. Par la suite, vous ne pourrez plus déchiffrer les messages/les fichiers chiffrés avec cette clé !</string>
|
||||
<string name="snack_security_token_other">Une clé différente est stockée sur le jeton de sécurité !</string>
|
||||
<string name="security_token_error">Erreur : %s</string>
|
||||
|
|
|
@ -1446,9 +1446,9 @@
|
|||
<string name="security_token_create">あなたのデバイスの背面にセキュリティトークンを保持してください。</string>
|
||||
<string name="security_token_reset_or_import">このセキュリティトークンにはすでに鍵が含まれています。 使用するには、追加の鍵の情報が必要です。 この情報は、鍵サーバ上で検索することや、ファイルからインポートすることができます。</string>
|
||||
<string name="btn_reset">リセット</string>
|
||||
<string name="security_token_import_radio">鍵サーバ上で鍵情報を検索</string>
|
||||
<string name="security_token_radio_use_existing">鍵サーバ上で鍵情報を検索</string>
|
||||
<string name="security_token_file_radio">ファイルから鍵情報をインポート</string>
|
||||
<string name="security_token_reset_radio">セキュリティトークンのリセット</string>
|
||||
<string name="security_token_radio_reset">セキュリティトークンのリセット</string>
|
||||
<string name="security_token_reset_warning">セキュリティトークンをリセットすると、その上の鍵を完全に破壊します。その後は、この鍵で暗号化されたメッセージ/ファイルを復号化することができなくなります!</string>
|
||||
<string name="snack_security_token_other">違う鍵がセキュリティトークンに格納されています!</string>
|
||||
<string name="security_token_error">エラー: %s</string>
|
||||
|
|
|
@ -580,7 +580,7 @@
|
|||
<string name="nfc_settings">Innstillinger</string>
|
||||
<string name="snack_security_token_view">Vis</string>
|
||||
<string name="btn_reset">Tilbakestill</string>
|
||||
<string name="security_token_reset_radio">Tilbakestill sikkerhetssymbol</string>
|
||||
<string name="security_token_radio_reset">Tilbakestill sikkerhetssymbol</string>
|
||||
<string name="security_token_error">Feil: %s</string>
|
||||
<string name="security_token_error_unknown">Ukjent feil</string>
|
||||
<string name="security_token_error_try_again">Prøv igjen</string>
|
||||
|
|
|
@ -1476,9 +1476,9 @@
|
|||
<string name="security_token_create">Hou het Security Token tegen de achterkant van je apparaat.</string>
|
||||
<string name="security_token_reset_or_import">Dit Security Token bevat al een sleutel. Om deze te gebruiken hebben we extra sleutelinformatie nodig. Deze informatie kan gevonden worden op een sleutelserver, of geïmporteerd uit een bestand.</string>
|
||||
<string name="btn_reset">Reset</string>
|
||||
<string name="security_token_import_radio">Zoek sleutelinformatie op sleutelserver</string>
|
||||
<string name="security_token_radio_use_existing">Zoek sleutelinformatie op sleutelserver</string>
|
||||
<string name="security_token_file_radio">Importeer sleutelinformatie uit bestand</string>
|
||||
<string name="security_token_reset_radio">Reset Security Token</string>
|
||||
<string name="security_token_radio_reset">Reset Security Token</string>
|
||||
<string name="security_token_reset_warning">Het opnieuw instellen van het Security Token maakt alle sleutels op het token ongedaan. Het is daarna niet meer mogelijk om berichten/bestanden te ontsleutelen met deze sleutel!</string>
|
||||
<string name="snack_security_token_other">Andere sleutel opgeslagen op Security Token!</string>
|
||||
<string name="security_token_error">Error: %s</string>
|
||||
|
|
|
@ -1390,7 +1390,7 @@
|
|||
<string name="security_token_status_partly">O Token de Segurança coincide, parcialmente associado à chave</string>
|
||||
<string name="security_token_create">Segure o Token de Segurança contra as costas do seu dispositivo.</string>
|
||||
<string name="btn_reset">Redefinir</string>
|
||||
<string name="security_token_reset_radio">Redefinir Token de Segurança</string>
|
||||
<string name="security_token_radio_reset">Redefinir Token de Segurança</string>
|
||||
<string name="security_token_reset_warning">Resetar o Token de Segurança destrói completamente as chaves nele. Após isto, você não será capaz de decriptar mensagens/arquivos encruiptados com estas chaves!</string>
|
||||
<string name="snack_security_token_other">Uma chave diferente está armazenada no Token de Segurança!</string>
|
||||
<string name="security_token_error">Erro: %s</string>
|
||||
|
|
|
@ -1544,9 +1544,9 @@
|
|||
<string name="security_token_create">Держите токен безопасности возле задней части вашего устройства.</string>
|
||||
<string name="security_token_reset_or_import">Этот токен безопасности уже содержит ключ. Чтобы его использовать, нужна дополнительная информация о ключе. Её можно найти на сервере ключей или импортировать из файла.</string>
|
||||
<string name="btn_reset">Сброс</string>
|
||||
<string name="security_token_import_radio">Найти информацию о ключе на сервере ключей</string>
|
||||
<string name="security_token_radio_use_existing">Найти информацию о ключе на сервере ключей</string>
|
||||
<string name="security_token_file_radio">Импортировать информацию о ключе из файла</string>
|
||||
<string name="security_token_reset_radio">Сбросить токен безопасности</string>
|
||||
<string name="security_token_radio_reset">Сбросить токен безопасности</string>
|
||||
<string name="security_token_reset_warning">Сброс токена безопасности полностью уничтожает ключи на нём. После этого вы не сможете расшифровать сообщения или файлы, зашифрованные с помощью данных ключей!</string>
|
||||
<string name="snack_security_token_other">На токене безопасности хранится другой ключ!</string>
|
||||
<string name="security_token_error">Ошибка: %s</string>
|
||||
|
|
|
@ -1512,9 +1512,9 @@
|
|||
<string name="security_token_create">Тримайте маркер безпеки навпроти зворотнього боку Вашого пристрою.</string>
|
||||
<string name="security_token_reset_or_import">Маркер безпеки вже містить ключ. Щоб використовувати його, нам потрібна додаткова інформація. Ця інформація може бути знайдена на сервері ключів чи імпортована з файлу.</string>
|
||||
<string name="btn_reset">Скинути</string>
|
||||
<string name="security_token_import_radio">Пошук інформації про ключ на сервері ключів</string>
|
||||
<string name="security_token_radio_use_existing">Пошук інформації про ключ на сервері ключів</string>
|
||||
<string name="security_token_file_radio">Імпорт інформації про ключ з файлу</string>
|
||||
<string name="security_token_reset_radio">Очистити маркер безпеки</string>
|
||||
<string name="security_token_radio_reset">Очистити маркер безпеки</string>
|
||||
<string name="security_token_reset_warning">Скидання маркера безпеки повністю знищить всі ключі на ньому. Ви не зможете розшифрувати повідомлення/файли зашифровані цим ключем.</string>
|
||||
<string name="snack_security_token_other">Різні ключі збережено на маркері безпеки!</string>
|
||||
<string name="security_token_error">Помилка: %s</string>
|
||||
|
|
|
@ -1359,7 +1359,7 @@
|
|||
<string name="security_token_status_partly">安全信息匹配并且已部分绑定到密钥</string>
|
||||
<string name="security_token_create">保持安全令牌在您的手机背部</string>
|
||||
<string name="btn_reset">重置</string>
|
||||
<string name="security_token_reset_radio">重置安全令牌</string>
|
||||
<string name="security_token_radio_reset">重置安全令牌</string>
|
||||
<string name="security_token_reset_warning">重置安全令牌将完全摧毁其内部的密钥。之后您将无法使用该密钥加解密消息或文件!</string>
|
||||
<string name="snack_security_token_other">安全令牌中存有不同的密钥!</string>
|
||||
<string name="security_token_error">错误: %s</string>
|
||||
|
|
|
@ -1593,11 +1593,10 @@
|
|||
<string name="security_token_status_unbound">"Security Token matches, can be bound to key"</string>
|
||||
<string name="security_token_status_partly">"Security Token matches, partly bound to key"</string>
|
||||
<string name="security_token_create">"Hold Security Token against the back of your device."</string>
|
||||
<string name="security_token_reset_or_import">"This Security Token already contains a key. To use it, we need additional key information. This information can be searched for on a keyserver or imported from a file."</string>
|
||||
<string name="security_token_reset_or_import">"This Security Token already contains a key."</string>
|
||||
<string name="btn_reset">"Reset"</string>
|
||||
<string name="security_token_import_radio">"Search key information on keyserver"</string>
|
||||
<string name="security_token_file_radio">"Import key information from file"</string>
|
||||
<string name="security_token_reset_radio">"Reset Security Token"</string>
|
||||
<string name="security_token_radio_use_existing">"Use existing key"</string>
|
||||
<string name="security_token_radio_reset">"Reset token"</string>
|
||||
<string name="security_token_reset_warning">"Resetting the Security Token completely destroys the keys on it. Afterwards, you will not be able to decrypt messages/files encrypted with this key!"</string>
|
||||
<string name="snack_security_token_other">Different key stored on Security Token!</string>
|
||||
<string name="security_token_error">"Error: %s"</string>
|
||||
|
@ -1919,4 +1918,11 @@
|
|||
<string name="transfer_error_wifi_text_instructions">"Make sure you are on the same network, then scan again."</string>
|
||||
<string name="transfer_error_wifi_text_instructions_ssid">"Make sure you are on the "%s" network, then scan again."</string>
|
||||
|
||||
<string name="status_search_local">Searching in key list…</string>
|
||||
<string name="status_search_uri">Searching at token Uri…</string>
|
||||
<string name="status_search_keyserver">Searching on keyservers…</string>
|
||||
<string name="status_import">Importing key…</string>
|
||||
<string name="status_token_promote">Setting up key…</string>
|
||||
<string name="status_token_check">Checking key setup…</string>
|
||||
|
||||
</resources>
|
||||
|
|
1
graphics/drawables/ic_bomb.svg
Normal file
1
graphics/drawables/ic_bomb.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M11.25,6A3.25,3.25 0 0,1 14.5,2.75A3.25,3.25 0 0,1 17.75,6C17.75,6.42 18.08,6.75 18.5,6.75C18.92,6.75 19.25,6.42 19.25,6V5.25H20.75V6A2.25,2.25 0 0,1 18.5,8.25A2.25,2.25 0 0,1 16.25,6A1.75,1.75 0 0,0 14.5,4.25A1.75,1.75 0 0,0 12.75,6H14V7.29C16.89,8.15 19,10.83 19,14A7,7 0 0,1 12,21A7,7 0 0,1 5,14C5,10.83 7.11,8.15 10,7.29V6H11.25M22,6H24V7H22V6M19,4V2H20V4H19M20.91,4.38L22.33,2.96L23.04,3.67L21.62,5.09L20.91,4.38Z" /></svg>
|
After Width: | Height: | Size: 713 B |
|
@ -22,7 +22,7 @@ SRC_DIR=./drawables/
|
|||
#inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg
|
||||
|
||||
|
||||
for NAME in "ic_live_help" "ic_send" "ic_cloud_unknown" "ic_cloud_off" "ic_wifi_lock" "broken_heart" "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link"
|
||||
for NAME in "ic_bomb" "ic_live_help" "ic_send" "ic_cloud_unknown" "ic_cloud_off" "ic_wifi_lock" "broken_heart" "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link"
|
||||
do
|
||||
echo $NAME
|
||||
inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME}_24dp.png" "$SRC_DIR/$NAME.svg"
|
||||
|
|
Loading…
Add table
Reference in a new issue