diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked')
12 files changed, 2160 insertions, 0 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java new file mode 100644 index 000000000..6b3eef26a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Patterns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; + +public class LinkedIdCreateDnsStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditDns; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateDnsStep1Fragment newInstance() { + LinkedIdCreateDnsStep1Fragment frag = new LinkedIdCreateDnsStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_dns_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + String uri = mEditDns.getText().toString(); + + if (!checkUri(uri)) { + mEditDns.setError("Please enter a valid domain name!"); + return; + } + + String proofText = DnsResource.generateText(getActivity(), + mLinkedIdWizard.mFingerprint); + + LinkedIdCreateDnsStep2Fragment frag = + LinkedIdCreateDnsStep2Fragment.newInstance(uri, proofText); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditDns = (EditText) view.findViewById(R.id.linked_create_dns_domain); + + mEditDns.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + } + + @Override + public void afterTextChanged(Editable editable) { + String uri = editable.toString(); + if (uri.length() > 0) { + if (checkUri(uri)) { + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_ok, 0); + } else { + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_bad, 0); + } + } else { + // remove drawable if email is empty + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + } + }); + + mEditDns.setText("test.mugenguild.com"); + + return view; + } + + private static boolean checkUri(String uri) { + return Patterns.DOMAIN_NAME.matcher(uri).matches(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java new file mode 100644 index 000000000..e8668dc90 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.util.FileHelper; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; + +public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + public static final String DOMAIN = "domain", TEXT = "text"; + + TextView mTextView; + + String mResourceDomain; + String mResourceString; + + public static LinkedIdCreateDnsStep2Fragment newInstance + (String uri, String proofText) { + + LinkedIdCreateDnsStep2Fragment frag = new LinkedIdCreateDnsStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(DOMAIN, uri); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mResourceDomain = getArguments().getString(DOMAIN); + mResourceString = getArguments().getString(TEXT); + + } + + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofToClipboard(); + } + }); + + mTextView = (TextView) view.findViewById(R.id.linked_create_dns_text); + mTextView.setText(mResourceString); + + return view; + } + + @Override + LinkedCookieResource getResource(OperationLog log) { + return DnsResource.createNew(mResourceDomain); + } + + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofToClipboard() { + ClipboardReflection.copyToClipboard(getActivity(), mResourceString); + Notify.create(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK).show(); + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.create(getActivity(), "Error writing file!", Style.ERROR).show(); + } + } catch (FileNotFoundException e) { + Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show(); + e.printStackTrace(); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + // For saving a file + case REQUEST_CODE_OUTPUT: + if (data == null) { + return; + } + Uri uri = data.getData(); + saveFile(uri); + break; + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java new file mode 100644 index 000000000..0767aabd2 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java @@ -0,0 +1,271 @@ +package org.sufficientlysecure.keychain.ui.linked; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.PorterDuff; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.ViewAnimator; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.ServiceProgressHandler; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment.ServiceType; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Passphrase; + +public abstract class LinkedIdCreateFinalFragment extends Fragment { + + protected static final int REQUEST_CODE_PASSPHRASE = 0x00007008; + + protected LinkedIdWizard mLinkedIdWizard; + + private ImageView mVerifyImage; + private TextView mVerifyStatus; + private ViewAnimator mVerifyAnimator; + + // This is a resource, set AFTER it has been verified + LinkedCookieResource mVerifiedResource = null; + private ViewAnimator mVerifyButtonAnimator; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + + protected abstract View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = newView(inflater, container, savedInstanceState); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startCertify(); + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mVerifyAnimator = (ViewAnimator) view.findViewById(R.id.verify_progress); + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + mVerifyButtonAnimator = (ViewAnimator) view.findViewById(R.id.verify_buttons); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + view.findViewById(R.id.button_retry).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + return view; + } + + abstract LinkedCookieResource getResource(OperationLog log); + + private void setVerifyProgress(boolean on, Boolean success) { + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + displayButton(on ? 2 : 0); + } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout_24dp); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + displayButton(2); + } else { + mVerifyStatus.setText(R.string.linked_verify_error); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout_24dp); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + displayButton(1); + } + mVerifyAnimator.setDisplayedChild(on ? 1 : 0); + } + + public void displayButton(int button) { + if (mVerifyButtonAnimator.getDisplayedChild() == button) { + return; + } + mVerifyButtonAnimator.setDisplayedChild(button); + } + + protected void proofVerify() { + setVerifyProgress(true, null); + + new AsyncTask<Void,Void,LinkedVerifyResult>() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + long timer = System.currentTimeMillis(); + + OperationLog log = new OperationLog(); + LinkedCookieResource resource = getResource(log); + if (resource == null) { + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + + LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint); + + // ux flow: this operation should take at last a second + timer = System.currentTimeMillis() -timer; + if (timer < 1000) try { + Thread.sleep(1000 -timer); + } catch (InterruptedException e) { + // never mind + } + + if (result.success()) { + mVerifiedResource = resource; + } + return result; + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + setVerifyProgress(false, true); + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } + }.execute(); + + } + + private void startCertify() { + + if (mVerifiedResource == null) { + Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR).show(); + return; + } + + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + + } + + private void certifyLinkedIdentity (CryptoInputParcel cryptoInput) { + ServiceProgressHandler saveHandler = new ServiceProgressHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true, ServiceType.KEYCHAIN_INTENT) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == MessageStatus.OKAY.ordinal()) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + getActivity().finish(); + + } + } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + CryptoInputParcel cryptoInput = + data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT); + certifyLinkedIdentity(cryptoInput); + } + break; + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java new file mode 100644 index 000000000..77eccf3be --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.util.Notify; + + +public class LinkedIdCreateGithubStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditHandle; + + public static LinkedIdCreateGithubStep1Fragment newInstance() { + LinkedIdCreateGithubStep1Fragment frag = new LinkedIdCreateGithubStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_github_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + final String handle = mEditHandle.getText().toString(); + + new AsyncTask<Void,Void,Boolean>() { + + @Override + protected Boolean doInBackground(Void... params) { + return true; // return checkHandle(handle); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + + if (result == null) { + Notify.create(getActivity(), + "Connection error while checking username!", Notify.Style.ERROR).show(); + return; + } + + if (!result) { + Notify.create(getActivity(), + "This handle does not exist on Github!", Notify.Style.ERROR).show(); + return; + } + + LinkedIdCreateGithubStep2Fragment frag = + LinkedIdCreateGithubStep2Fragment.newInstance(handle); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }.execute(); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditHandle = (EditText) view.findViewById(R.id.linked_create_github_handle); + mEditHandle.setText("Valodim"); + + return view; + } + + private static Boolean checkHandle(String handle) { + try { + HttpURLConnection nection = + (HttpURLConnection) new URL("https://api.github.com/" + handle).openConnection(); + nection.setRequestMethod("HEAD"); + return nection.getResponseCode() == 200; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java new file mode 100644 index 000000000..cc1052b1f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.GithubResource; + + +public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragment { + + public static final String ARG_HANDLE = "handle"; + + String mResourceHandle; + String mResourceString; + + public static LinkedIdCreateGithubStep2Fragment newInstance + (String handle) { + + LinkedIdCreateGithubStep2Fragment frag = new LinkedIdCreateGithubStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(ARG_HANDLE, handle); + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mResourceString = + GithubResource.generate(getActivity(), mLinkedIdWizard.mFingerprint); + + mResourceHandle = getArguments().getString(ARG_HANDLE); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_share).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofShare(); + } + }); + + return view; + } + + @Override + LinkedCookieResource getResource(OperationLog log) { + return GithubResource.searchInGithubStream(mResourceHandle, mResourceString, log); + } + + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_github_fragment_step2, container, false); + } + + private void proofShare() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSend() { + Uri.Builder builder = Uri.parse("https://gist.github.com/").buildUpon(); + builder.appendQueryParameter("text", mResourceString); + Uri uri = builder.build(); + + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + getActivity().startActivity(intent); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java new file mode 100644 index 000000000..72669142b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Patterns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; + +public class LinkedIdCreateHttpsStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditUri; + + public static LinkedIdCreateHttpsStep1Fragment newInstance() { + LinkedIdCreateHttpsStep1Fragment frag = new LinkedIdCreateHttpsStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_https_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + String uri = "https://" + mEditUri.getText(); + + if (!checkUri(uri)) { + return; + } + + String proofText = GenericHttpsResource.generateText(getActivity(), + mLinkedIdWizard.mFingerprint); + + LinkedIdCreateHttpsStep2Fragment frag = + LinkedIdCreateHttpsStep2Fragment.newInstance(uri, proofText); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); + + mEditUri.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + } + + @Override + public void afterTextChanged(Editable editable) { + String uri = "https://" + editable; + if (uri.length() > 0) { + if (checkUri(uri)) { + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_ok, 0); + } else { + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_bad, 0); + } + } else { + // remove drawable if email is empty + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + } + }); + + mEditUri.setText("mugenguild.com/pgpkey.txt"); + + return view; + } + + private static boolean checkUri(String uri) { + return Patterns.WEB_URL.matcher(uri).matches(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java new file mode 100644 index 000000000..6bb555105 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.util.FileHelper; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; + +public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + public static final String ARG_URI = "uri", ARG_TEXT = "text"; + + EditText mEditUri; + + URI mResourceUri; + String mResourceString; + + public static LinkedIdCreateHttpsStep2Fragment newInstance + (String uri, String proofText) { + + LinkedIdCreateHttpsStep2Fragment frag = new LinkedIdCreateHttpsStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(ARG_URI, uri); + args.putString(ARG_TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + GenericHttpsResource getResource(OperationLog log) { + return GenericHttpsResource.createNew(mResourceUri); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + mResourceUri = new URI(getArguments().getString(ARG_URI)); + } catch (URISyntaxException e) { + e.printStackTrace(); + getActivity().finish(); + } + + mResourceString = getArguments().getString(ARG_TEXT); + + } + + protected View newView(LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSave(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); + mEditUri.setText(mResourceUri.toString()); + + return view; + } + + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.create(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.create(getActivity(), "Error writing file!", Style.ERROR).show(); + } + } catch (FileNotFoundException e) { + Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show(); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + // For saving a file + case REQUEST_CODE_OUTPUT: + if (data == null) { + return; + } + Uri uri = data.getData(); + saveFile(uri); + break; + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java new file mode 100644 index 000000000..c36f98058 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +public class LinkedIdCreateTwitterStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditHandle; + + public static LinkedIdCreateTwitterStep1Fragment newInstance() { + LinkedIdCreateTwitterStep1Fragment frag = new LinkedIdCreateTwitterStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + final String handle = mEditHandle.getText().toString(); + + new AsyncTask<Void,Void,Boolean>() { + + @Override + protected Boolean doInBackground(Void... params) { + return true; // return checkHandle(handle); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + + if (result == null) { + Notify.create(getActivity(), + "Connection error while checking username!", Notify.Style.ERROR); + return; + } + + if (!result) { + Notify.create(getActivity(), + "This handle does not exist on Twitter!", Notify.Style.ERROR); + return; + } + + LinkedIdCreateTwitterStep2Fragment frag = + LinkedIdCreateTwitterStep2Fragment.newInstance(handle); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }.execute(); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); + mEditHandle.setText("v_debug"); + + return view; + } + + private static Boolean checkHandle(String handle) { + try { + HttpURLConnection nection = + (HttpURLConnection) new URL("https://twitter.com/" + handle).openConnection(); + nection.setRequestMethod("HEAD"); + return nection.getResponseCode() == 200; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java new file mode 100644 index 000000000..66e86f7d4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; + +public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragment { + + public static final String ARG_HANDLE = "handle"; + + String mResourceHandle; + String mResourceString; + + public static LinkedIdCreateTwitterStep2Fragment newInstance + (String handle) { + + LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(ARG_HANDLE, handle); + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mResourceString = + TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint); + + mResourceHandle = getArguments().getString(ARG_HANDLE); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_share).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofShare(); + } + }); + + return view; + } + + @Override + LinkedCookieResource getResource(OperationLog log) { + return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString, log); + } + + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); + } + + private void proofShare() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSend() { + + Uri.Builder builder = Uri.parse("https://twitter.com/intent/tweet").buildUpon(); + builder.appendQueryParameter("text", mResourceString); + Uri uri = builder.build(); + + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + getActivity().startActivity(intent); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java new file mode 100644 index 000000000..8249f0bb6 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.R; + +public class LinkedIdSelectFragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdSelectFragment newInstance() { + LinkedIdSelectFragment frag = new LinkedIdSelectFragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.linked_select_fragment, container, false); + + view.findViewById(R.id.linked_create_https_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateHttpsStep1Fragment frag = + LinkedIdCreateHttpsStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.linked_create_dns_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateDnsStep1Fragment frag = + LinkedIdCreateDnsStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.linked_create_twitter_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateTwitterStep1Fragment frag = + LinkedIdCreateTwitterStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.linked_create_github_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateGithubStep1Fragment frag = + LinkedIdCreateGithubStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java new file mode 100644 index 000000000..1944146dd --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java @@ -0,0 +1,611 @@ +package org.sufficientlysecure.keychain.ui.linked; + +import java.io.IOException; +import java.util.Collections; + +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentManager.OnBackStackChangedListener; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextSwitcher; +import android.widget.ViewAnimator; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Constants.key; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.CertifyResult; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.service.ServiceProgressHandler; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; +import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.ViewHolder.VerifyState; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +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.SubtleAttentionSeeker; +import org.sufficientlysecure.keychain.ui.widget.CertListWidget; +import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; + + +public class LinkedIdViewFragment extends Fragment implements + LoaderManager.LoaderCallbacks<Cursor>, OnBackStackChangedListener { + + public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; + + private static final String ARG_DATA_URI = "data_uri"; + private static final String ARG_LID_RANK = "rank"; + private static final String ARG_IS_SECRET = "verified"; + private static final String ARG_FINGERPRINT = "fingerprint"; + private static final int LOADER_ID_LINKED_ID = 1; + + private RawLinkedIdentity mLinkedId; + private LinkedCookieResource mLinkedResource; + private boolean mIsSecret; + + private Context mContext; + private byte[] mFingerprint; + + private AsyncTask mInProgress; + + private Uri mDataUri; + private ViewHolder mViewHolder; + private int mLidRank; + private OnIdentityLoadedListener mIdLoadedListener; + private long mCertifyKeyId; + + public static LinkedIdViewFragment newInstance(Uri dataUri, int rank, + boolean isSecret, byte[] fingerprint) throws IOException { + LinkedIdViewFragment frag = new LinkedIdViewFragment(); + + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + args.putInt(ARG_LID_RANK, rank); + args.putBoolean(ARG_IS_SECRET, isSecret); + args.putByteArray(ARG_FINGERPRINT, fingerprint); + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + mDataUri = args.getParcelable(ARG_DATA_URI); + mLidRank = args.getInt(ARG_LID_RANK); + + mIsSecret = args.getBoolean(ARG_IS_SECRET); + mFingerprint = args.getByteArray(ARG_FINGERPRINT); + + mContext = getActivity(); + + getLoaderManager().initLoader(LOADER_ID_LINKED_ID, null, this); + + } + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + switch (id) { + case LOADER_ID_LINKED_ID: + return new CursorLoader(getActivity(), mDataUri, + UserIdsAdapter.USER_PACKETS_PROJECTION, + Tables.USER_PACKETS + "." + UserPackets.RANK + + " = " + Integer.toString(mLidRank), null, null); + default: + return null; + } + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { + switch (loader.getId()) { + case LOADER_ID_LINKED_ID: + + // Nothing to load means break if we are *expected* to load + if (!cursor.moveToFirst()) { + if (mIdLoadedListener != null) { + Notify.create(getActivity(), "Error loading identity!", + Notify.LENGTH_LONG, Style.ERROR).show(); + finishFragment(); + } + // Or just ignore, this is probably some intermediate state during certify + break; + } + + try { + int certStatus = cursor.getInt(UserIdsAdapter.INDEX_VERIFIED); + + byte[] data = cursor.getBlob(UserIdsAdapter.INDEX_ATTRIBUTE_DATA); + RawLinkedIdentity linkedId = LinkedIdentity.fromAttributeData(data); + + loadIdentity(linkedId, certStatus); + + if (mIdLoadedListener != null) { + mIdLoadedListener.onIdentityLoaded(); + mIdLoadedListener = null; + } + + } catch (IOException e) { + Log.e(Constants.TAG, "error parsing identity", e); + Notify.create(getActivity(), "Error parsing identity!", + Notify.LENGTH_LONG, Style.ERROR).show(); + finishFragment(); + } + + break; + } + } + + public void finishFragment() { + new Handler().post(new Runnable() { + @Override + public void run() { + FragmentManager manager = getFragmentManager(); + manager.removeOnBackStackChangedListener(LinkedIdViewFragment.this); + manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + }); + } + + public interface OnIdentityLoadedListener { + void onIdentityLoaded(); + } + + public void setOnIdentityLoadedListener(OnIdentityLoadedListener listener) { + mIdLoadedListener = listener; + } + + private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { + mLinkedId = linkedId; + + if (mLinkedId instanceof LinkedIdentity) { + LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; + mLinkedResource = (LinkedCookieResource) res; + } + + if (!mIsSecret) { + switch (certStatus) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, + null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, + null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, + null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); + break; + } + } + + mViewHolder.mLinkedIdHolder.setData(mContext, mLinkedId); + + setShowVerifying(false); + + // no resource, nothing further we can do… + if (mLinkedResource == null) { + mViewHolder.vButtonView.setVisibility(View.GONE); + mViewHolder.vButtonVerify.setVisibility(View.GONE); + return; + } + + if (mLinkedResource.isViewable()) { + mViewHolder.vButtonView.setVisibility(View.VISIBLE); + mViewHolder.vButtonView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = mLinkedResource.getViewIntent(); + if (intent == null) { + return; + } + getActivity().startActivity(intent); + } + }); + } else { + mViewHolder.vButtonView.setVisibility(View.GONE); + } + + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + + } + + static class ViewHolder { + private final View vButtonView; + private final ViewAnimator vVerifyingContainer; + private final ViewAnimator vItemCertified; + private final View vKeySpinnerContainer; + LinkedIdsAdapter.ViewHolder mLinkedIdHolder; + + private ViewAnimator vButtonSwitcher; + private CertListWidget vLinkedCerts; + private CertifyKeySpinner vKeySpinner; + private final View vButtonVerify; + private final View vButtonRetry; + private final View vButtonConfirm; + + private final ViewAnimator vProgress; + private final TextSwitcher vText; + + ViewHolder(View root) { + vLinkedCerts = (CertListWidget) root.findViewById(R.id.linked_id_certs); + vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); + vKeySpinnerContainer = root.findViewById(R.id.cert_key_spincontainer); + vButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); + + mLinkedIdHolder = new LinkedIdsAdapter.ViewHolder(root); + + vButtonVerify = root.findViewById(R.id.button_verify); + vButtonRetry = root.findViewById(R.id.button_retry); + vButtonConfirm = root.findViewById(R.id.button_confirm); + vButtonView = root.findViewById(R.id.button_view); + + vVerifyingContainer = (ViewAnimator) root.findViewById(R.id.linked_verify_container); + vItemCertified = (ViewAnimator) root.findViewById(R.id.linked_id_certified); + + vProgress = (ViewAnimator) root.findViewById(R.id.linked_cert_progress); + vText = (TextSwitcher) root.findViewById(R.id.linked_cert_text); + } + + enum VerifyState { + VERIFYING, VERIFY_OK, VERIFY_ERROR, CERTIFYING + } + + void setVerifyingState(Context context, VerifyState state, boolean isSecret) { + switch (state) { + case VERIFYING: + vProgress.setDisplayedChild(0); + vText.setText(context.getString(R.string.linked_text_verifying)); + vKeySpinnerContainer.setVisibility(View.GONE); + break; + + case VERIFY_OK: + vProgress.setDisplayedChild(1); + if (!isSecret) { + showButton(2); + vKeySpinnerContainer.setVisibility(View.VISIBLE); + } else { + showButton(1); + vKeySpinnerContainer.setVisibility(View.GONE); + } + break; + + case VERIFY_ERROR: + showButton(1); + vProgress.setDisplayedChild(2); + vText.setText(context.getString(R.string.linked_text_error)); + vKeySpinnerContainer.setVisibility(View.GONE); + break; + + case CERTIFYING: + vProgress.setDisplayedChild(0); + vText.setText(context.getString(R.string.linked_text_confirming)); + vKeySpinnerContainer.setVisibility(View.GONE); + break; + } + } + + void showVerifyingContainer(Context context, boolean show, boolean isSecret) { + if (vVerifyingContainer.getDisplayedChild() == (show ? 1 : 0)) { + return; + } + + vVerifyingContainer.setInAnimation(context, show ? R.anim.fade_in_up : R.anim.fade_in_down); + vVerifyingContainer.setOutAnimation(context, show ? R.anim.fade_out_up : R.anim.fade_out_down); + vVerifyingContainer.setDisplayedChild(show ? 1 : 0); + + vItemCertified.setInAnimation(context, show ? R.anim.fade_in_up : R.anim.fade_in_down); + vItemCertified.setOutAnimation(context, show ? R.anim.fade_out_up : R.anim.fade_out_down); + vItemCertified.setDisplayedChild(show || isSecret ? 1 : 0); + } + + void showButton(int which) { + if (vButtonSwitcher.getDisplayedChild() == which) { + return; + } + vButtonSwitcher.setDisplayedChild(which); + } + + } + + private boolean mVerificationState = false; + /** Switches between the 'verifying' ui bit and certificate status. This method + * must behave correctly in all states, showing or hiding the appropriate views + * and cancelling pending operations where necessary. + * + * This method also handles back button functionality in combination with + * onBackStateChanged. + */ + void setShowVerifying(boolean show) { + if (!show) { + if (mInProgress != null) { + mInProgress.cancel(false); + mInProgress = null; + } + getFragmentManager().removeOnBackStackChangedListener(this); + new Handler().post(new Runnable() { + @Override + public void run() { + getFragmentManager().popBackStack("verification", + FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + }); + + if (!mVerificationState) { + return; + } + mVerificationState = false; + + mViewHolder.showButton(0); + mViewHolder.vKeySpinnerContainer.setVisibility(View.GONE); + mViewHolder.showVerifyingContainer(mContext, false, mIsSecret); + return; + } + + if (mVerificationState) { + return; + } + mVerificationState = true; + + FragmentManager manager = getFragmentManager(); + manager.beginTransaction().addToBackStack("verification").commit(); + manager.executePendingTransactions(); + manager.addOnBackStackChangedListener(this); + mViewHolder.showVerifyingContainer(mContext, true, mIsSecret); + + } + + @Override + public void onBackStackChanged() { + setShowVerifying(false); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.linked_id_view_fragment, null); + + mViewHolder = new ViewHolder(root); + root.setTag(mViewHolder); + + ((ImageView) root.findViewById(R.id.status_icon_verified)) + .setColorFilter(mContext.getResources().getColor(R.color.android_green_light), + PorterDuff.Mode.SRC_IN); + ((ImageView) root.findViewById(R.id.status_icon_invalid)) + .setColorFilter(mContext.getResources().getColor(R.color.android_red_light), + PorterDuff.Mode.SRC_IN); + + mViewHolder.vButtonVerify.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + verifyResource(); + } + }); + mViewHolder.vButtonRetry.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + verifyResource(); + } + }); + mViewHolder.vButtonConfirm.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + initiateCertifying(); + } + }); + + { + Bundle args = new Bundle(); + args.putParcelable(CertListWidget.ARG_URI, Certs.buildLinkedIdCertsUri(mDataUri, mLidRank)); + args.putBoolean(CertListWidget.ARG_IS_SECRET, mIsSecret); + getLoaderManager().initLoader(CertListWidget.LOADER_ID_LINKED_CERTS, + args, mViewHolder.vLinkedCerts); + } + + return root; + } + + void verifyResource() { + + // only one at a time (no sync needed, mInProgress is only touched in ui thread) + if (mInProgress != null) { + return; + } + + setShowVerifying(true); + + mViewHolder.vKeySpinnerContainer.setVisibility(View.GONE); + mViewHolder.setVerifyingState(mContext, VerifyState.VERIFYING, mIsSecret); + + mInProgress = new AsyncTask<Void,Void,LinkedVerifyResult>() { + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + long timer = System.currentTimeMillis(); + LinkedVerifyResult result = mLinkedResource.verify(mFingerprint); + + // ux flow: this operation should take at last a second + timer = System.currentTimeMillis() -timer; + if (timer < 1000) try { + Thread.sleep(1000 -timer); + } catch (InterruptedException e) { + // never mind + } + + return result; + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + if (isCancelled()) { + return; + } + if (result.success()) { + mViewHolder.vText.setText(getString(mLinkedResource.getVerifiedText(mIsSecret))); + mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_OK, mIsSecret); + ObjectAnimator anim = SubtleAttentionSeeker.tada(mViewHolder.vButtonConfirm, 0.6f, 1000); + anim.setStartDelay(800); + anim.start(); + } else { + mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_ERROR, mIsSecret); + result.createNotify(getActivity()).show(); + } + mInProgress = null; + } + }.execute(); + + } + + private void initiateCertifying() { + + if (mIsSecret) { + return; + } + + // get the user's passphrase for this key (if required) + mCertifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); + if (mCertifyKeyId == key.none || mCertifyKeyId == key.symmetric) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + SubtleAttentionSeeker.tint(mViewHolder.vKeySpinnerContainer, 600).start(); + } else { + Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); + } + return; + } + + Passphrase passphrase; + try { + passphrase = PassphraseCacheService.getCachedPassphrase( + getActivity(), mCertifyKeyId, mCertifyKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + Log.e(Constants.TAG, "Key not found!", e); + getActivity().finish(); + return; + } + if (passphrase == null) { + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mCertifyKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + // bail out; need to wait until the user has entered the passphrase before trying again + } else { + certifyResource(new CryptoInputParcel()); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PASSPHRASE: { + if (resultCode == Activity.RESULT_OK && data != null) { + CryptoInputParcel cryptoInput = data.getParcelableExtra( + PassphraseDialogActivity.RESULT_CRYPTO_INPUT); + certifyResource(cryptoInput); + } + return; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + } + } + } + + private void certifyResource(CryptoInputParcel cryptoInput) { + + if (mIsSecret) { + return; + } + + mViewHolder.setVerifyingState(mContext, VerifyState.CERTIFYING, false); + + Bundle data = new Bundle(); + { + + long masterKeyId = KeyFormattingUtils.convertFingerprintToKeyId(mFingerprint); + CertifyAction action = new CertifyAction(masterKeyId, null, + Collections.singletonList(mLinkedId.toUserAttribute())); + + // fill values for this action + CertifyActionsParcel parcel = new CertifyActionsParcel(mCertifyKeyId); + parcel.mCertifyActions.addAll(Collections.singletonList(action)); + data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel); + + data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); + + /* if (mUploadKeyCheckbox.isChecked()) { + String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); + data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver); + } */ + } + + // Send all information needed to service to sign key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after signing is done in KeychainIntentService + ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + Bundle data = message.getData(); + + if (message.arg1 == MessageStatus.OKAY.ordinal()) { + CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + result.createNotify(getActivity()).show(); + // no need to do anything else, we will get a loader refresh! + } + + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // start service with intent + getActivity().startService(intent); + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java new file mode 100644 index 000000000..161efc8fb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.linked; + +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBarActivity; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; + +public class LinkedIdWizard extends ActionBarActivity { + + public static final int FRAG_ACTION_START = 0; + public static final int FRAG_ACTION_TO_RIGHT = 1; + public static final int FRAG_ACTION_TO_LEFT = 2; + + long mMasterKeyId; + byte[] mFingerprint; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.create_key_activity); + + try { + Uri uri = getIntent().getData(); + uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(uri); + CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(uri); + if (!ring.hasAnySecret()) { + Log.e(Constants.TAG, "Linked Identities can only be added to secret keys!"); + finish(); + return; + } + + mMasterKeyId = ring.extractOrGetMasterKeyId(); + mFingerprint = ring.getFingerprint(); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "Invalid uri given, key does not exist!"); + finish(); + return; + } + + // pass extras into fragment + LinkedIdSelectFragment frag = LinkedIdSelectFragment.newInstance(); + loadFragment(null, frag, FRAG_ACTION_START); + } + + public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + + switch (action) { + case FRAG_ACTION_START: + transaction.setCustomAnimations(0, 0); + transaction.replace(R.id.create_key_fragment_container, fragment) + .commitAllowingStateLoss(); + break; + case FRAG_ACTION_TO_LEFT: + getSupportFragmentManager().popBackStackImmediate(); + break; + case FRAG_ACTION_TO_RIGHT: + transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left, + R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right); + transaction.addToBackStack(null); + transaction.replace(R.id.create_key_fragment_container, fragment) + .commitAllowingStateLoss(); + break; + + } + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} |