split EncryptActivity into Encrypt{Text,File}Activity

This commit is contained in:
Vincent Breitmoser 2014-09-01 23:25:03 +02:00
parent e26d7be7de
commit 2ead78a1e5
16 changed files with 615 additions and 174 deletions

View File

@ -139,18 +139,12 @@
android:label="@string/title_select_secret_key" android:label="@string/title_select_secret_key"
android:launchMode="singleTop" /> android:launchMode="singleTop" />
<activity <activity
android:name=".ui.EncryptActivity" android:name=".ui.EncryptFileActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_encrypt" android:label="@string/title_encrypt_files"
android:windowSoftInputMode="stateHidden"> android:windowSoftInputMode="stateHidden">
<!-- Keychain's own Actions --> <!-- Keychain's own Actions -->
<!-- ENCRYPT with text as extra -->
<intent-filter>
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- ENCRYPT with data Uri --> <!-- ENCRYPT with data Uri -->
<intent-filter> <intent-filter>
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" /> <action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
@ -170,6 +164,20 @@
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".ui.EncryptTextActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_encrypt_text"
android:windowSoftInputMode="stateHidden">
<!-- Keychain's own Actions -->
<!-- ENCRYPT with text as extra -->
<intent-filter>
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity <activity
android:name=".ui.DecryptActivity" android:name=".ui.DecryptActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"

View File

@ -22,7 +22,8 @@ import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.remote.ui.AppsListActivity; import org.sufficientlysecure.keychain.remote.ui.AppsListActivity;
import org.sufficientlysecure.keychain.ui.DecryptActivity; import org.sufficientlysecure.keychain.ui.DecryptActivity;
import org.sufficientlysecure.keychain.ui.EncryptActivity; import org.sufficientlysecure.keychain.ui.EncryptFileActivity;
import org.sufficientlysecure.keychain.ui.EncryptTextActivity;
import org.sufficientlysecure.keychain.ui.KeyListActivity; import org.sufficientlysecure.keychain.ui.KeyListActivity;
import java.io.File; import java.io.File;
@ -84,12 +85,14 @@ public final class Constants {
public static final class DrawerItems { public static final class DrawerItems {
public static final Class KEY_LIST = KeyListActivity.class; public static final Class KEY_LIST = KeyListActivity.class;
public static final Class ENCRYPT = EncryptActivity.class; public static final Class ENCRYPT_FILE = EncryptFileActivity.class;
public static final Class ENCRYPT_TEXT = EncryptTextActivity.class;
public static final Class DECRYPT = DecryptActivity.class; public static final Class DECRYPT = DecryptActivity.class;
public static final Class REGISTERED_APPS_LIST = AppsListActivity.class; public static final Class REGISTERED_APPS_LIST = AppsListActivity.class;
public static final Class[] ARRAY = new Class[]{ public static final Class[] ARRAY = new Class[]{
KEY_LIST, KEY_LIST,
ENCRYPT, ENCRYPT_FILE,
ENCRYPT_TEXT,
DECRYPT, DECRYPT,
REGISTERED_APPS_LIST REGISTERED_APPS_LIST
}; };

View File

@ -79,7 +79,8 @@ public class DrawerActivity extends ActionBarActivity {
NavItem mItemIconTexts[] = new NavItem[]{ NavItem mItemIconTexts[] = new NavItem[]{
new NavItem(R.drawable.ic_action_person, getString(R.string.nav_keys)), new NavItem(R.drawable.ic_action_person, getString(R.string.nav_keys)),
new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt)), new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_files)),
new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_text)),
new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)), new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)),
new NavItem(R.drawable.ic_action_view_as_list, getString(R.string.nav_apps))}; new NavItem(R.drawable.ic_action_view_as_list, getString(R.string.nav_apps))};

View File

@ -20,18 +20,12 @@ package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.content.pm.LabeledIntent;
import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -43,27 +37,19 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify; import org.sufficientlysecure.keychain.util.Notify;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
public class EncryptActivity extends DrawerActivity implements EncryptActivityInterface { public class EncryptFileActivity extends DrawerActivity implements EncryptActivityInterface {
/* Intents */ /* Intents */
public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT"; public static final String ACTION_ENCRYPT_FILE = Constants.INTENT_PREFIX + "ENCRYPT_FILE";
/* EXTRA keys for input */
public static final String EXTRA_TEXT = "text";
// enables ASCII Armor for file encryption when uri is given // enables ASCII Armor for file encryption when uri is given
public static final String EXTRA_ASCII_ARMOR = "ascii_armor"; public static final String EXTRA_ASCII_ARMOR = "ascii_armor";
@ -73,18 +59,11 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids"; public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids";
// view // view
private int mCurrentMode = PAGER_MODE_ASYMMETRIC; private int mCurrentMode = MODE_ASYMMETRIC;
private ViewPager mViewPagerContent;
private PagerTabStrip mPagerTabStripContent;
private PagerTabStripAdapter mTabsAdapterContent;
// tabs // tabs
private int mSwitchToContent = PAGER_CONTENT_MESSAGE; private static final int MODE_ASYMMETRIC = 0;
private static final int MODE_SYMMETRIC = 1;
private static final int PAGER_MODE_ASYMMETRIC = 0;
private static final int PAGER_MODE_SYMMETRIC = 1;
private static final int PAGER_CONTENT_MESSAGE = 0;
private static final int PAGER_CONTENT_FILE = 1;
// model used by fragments // model used by fragments
private long mEncryptionKeyIds[] = null; private long mEncryptionKeyIds[] = null;
@ -99,11 +78,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
private String mMessage = ""; private String mMessage = "";
public boolean isModeSymmetric() { public boolean isModeSymmetric() {
return PAGER_MODE_SYMMETRIC == mCurrentMode; return MODE_SYMMETRIC == mCurrentMode;
}
public boolean isContentMessage() {
return PAGER_CONTENT_MESSAGE == mViewPagerContent.getCurrentItem();
} }
@Override @Override
@ -217,26 +192,24 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
super.handleMessage(message); super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
if (!isContentMessage()) { Notify.showNotify(EncryptFileActivity.this, R.string.encrypt_sign_successful, Notify.Style.INFO);
Notify.showNotify(EncryptActivity.this, R.string.encrypt_sign_successful, Notify.Style.INFO);
if (mDeleteAfterEncrypt) { if (mDeleteAfterEncrypt) {
for (Uri inputUri : mInputUris) { for (Uri inputUri : mInputUris) {
DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri); DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri);
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
}
mInputUris.clear();
notifyUpdate();
} }
mInputUris.clear();
notifyUpdate();
} }
if (mShareAfterEncrypt) { if (mShareAfterEncrypt) {
// Share encrypted message/file // Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt(message)); startActivity(sendWithChooserExcludingEncrypt(message));
} else if (isContentMessage()) { } else {
// Copy to clipboard // Copy to clipboard
copyToClipboard(message); copyToClipboard(message);
Notify.showNotify(EncryptActivity.this, Notify.showNotify(EncryptFileActivity.this,
R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
} }
} }
@ -257,25 +230,17 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
// fill values for this action // fill values for this action
Bundle data = new Bundle(); Bundle data = new Bundle();
if (isContentMessage()) { data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS);
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris);
data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes());
data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS);
Preferences.getPreferences(this).getDefaultMessageCompression()); data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris);
} else {
data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS);
data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris);
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS); data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris); Preferences.getPreferences(this).getDefaultFileCompression());
data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
Preferences.getPreferences(this).getDefaultFileCompression());
}
// Always use armor for messages // Always use armor for messages
data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor || isContentMessage()); data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor);
if (isModeSymmetric()) { if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!"); Log.d(Constants.TAG, "Symmetric encryption enabled!");
@ -306,8 +271,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
private Intent sendWithChooserExcludingEncrypt(Message message) { private Intent sendWithChooserExcludingEncrypt(Message message) {
Intent prototype = createSendIntent(message); Intent prototype = createSendIntent(message);
String title = isContentMessage() ? getString(R.string.title_share_message) String title = getString(R.string.title_share_file);
: getString(R.string.title_share_file);
// Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3 // Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3
// // fallback on Android 2.3, otherwise we would get weird results // // fallback on Android 2.3, otherwise we would get weird results
@ -370,21 +334,16 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
private Intent createSendIntent(Message message) { private Intent createSendIntent(Message message) {
Intent sendIntent; Intent sendIntent;
if (isContentMessage()) { // file
if (mOutputUris.size() == 1) {
sendIntent = new Intent(Intent.ACTION_SEND); sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain"); sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris.get(0));
sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
} else { } else {
// file sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
if (mOutputUris.size() == 1) { sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris);
sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris.get(0));
} else {
sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris);
}
sendIntent.setType("application/octet-stream");
} }
sendIntent.setType("application/octet-stream");
if (!isModeSymmetric() && mEncryptionUserIds != null) { if (!isModeSymmetric() && mEncryptionUserIds != null) {
Set<String> users = new HashSet<String>(); Set<String> users = new HashSet<String>();
for (String user : mEncryptionUserIds) { for (String user : mEncryptionUserIds) {
@ -399,24 +358,17 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
} }
private boolean inputIsValid() { private boolean inputIsValid() {
if (isContentMessage()) { // file checks
if (mMessage == null) {
Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR);
return false;
}
} else {
// file checks
if (mInputUris.isEmpty()) { if (mInputUris.isEmpty()) {
Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR); Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR);
return false; return false;
} else if (mInputUris.size() > 1 && !mShareAfterEncrypt) { } else if (mInputUris.size() > 1 && !mShareAfterEncrypt) {
// This should be impossible... // This should be impossible...
return false; return false;
} else if (mInputUris.size() != mOutputUris.size()) { } else if (mInputUris.size() != mOutputUris.size()) {
// This as well // This as well
return false; return false;
}
} }
if (isModeSymmetric()) { if (isModeSymmetric()) {
@ -438,7 +390,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
&& mEncryptionKeyIds.length > 0); && mEncryptionKeyIds.length > 0);
// Files must be encrypted, only text can be signed-only right now // Files must be encrypted, only text can be signed-only right now
if (!gotEncryptionKeys && !isContentMessage()) { if (!gotEncryptionKeys) {
Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR); Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR);
return false; return false;
} }
@ -471,24 +423,14 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
return true; return true;
} }
private void initView() {
mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content);
mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content);
mTabsAdapterContent = new PagerTabStripAdapter(this);
mViewPagerContent.setAdapter(mTabsAdapterContent);
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_activity); setContentView(R.layout.encrypt_file_activity);
initView();
// if called with an intent action, do not init drawer navigation // if called with an intent action, do not init drawer navigation
if (ACTION_ENCRYPT.equals(getIntent().getAction())) { if (ACTION_ENCRYPT_FILE.equals(getIntent().getAction())) {
// TODO: back button to key? // TODO: back button to key?
} else { } else {
setupDrawerNavigation(savedInstanceState); setupDrawerNavigation(savedInstanceState);
@ -498,16 +440,12 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
handleActions(getIntent()); handleActions(getIntent());
updateModeFragment(); updateModeFragment();
mTabsAdapterContent.addTab(EncryptMessageFragment.class, null, getString(R.string.label_message));
mTabsAdapterContent.addTab(EncryptFileFragment.class, null, getString(R.string.label_files));
mViewPagerContent.setCurrentItem(mSwitchToContent);
mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor(); mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor();
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.encrypt_activity, menu); getMenuInflater().inflate(R.menu.encrypt_file_activity, menu);
menu.findItem(R.id.check_use_armor).setChecked(mUseArmor); menu.findItem(R.id.check_use_armor).setChecked(mUseArmor);
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@ -515,7 +453,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
private void updateModeFragment() { private void updateModeFragment() {
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(R.id.encrypt_pager_mode, .replace(R.id.encrypt_pager_mode,
mCurrentMode == PAGER_MODE_SYMMETRIC mCurrentMode == MODE_SYMMETRIC
? new EncryptSymmetricFragment() ? new EncryptSymmetricFragment()
: new EncryptAsymmetricFragment()) : new EncryptAsymmetricFragment())
.commitAllowingStateLoss(); .commitAllowingStateLoss();
@ -529,7 +467,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
} }
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.check_use_symmetric: case R.id.check_use_symmetric:
mCurrentMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC; mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC;
updateModeFragment(); updateModeFragment();
notifyUpdate(); notifyUpdate();
break; break;
@ -569,58 +507,44 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
/* /*
* Android's Action * Android's Action
*/ */
if (Intent.ACTION_SEND.equals(action) && type != null) { if (Intent.ACTION_SEND.equals(action) && type != null) {
// When sending to OpenKeychain Encrypt via share menu // When sending to OpenKeychain Encrypt via share menu
if ("text/plain".equals(type)) { /* if ("text/plain".equals(type)) {
// Plain text // TODO handle, maybe forward to "encrypt text" activity?
Plain text
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) { if (sharedText != null) {
// handle like normal text encryption, override action and extras to later // handle like normal text encryption, override action and extras to later
// executeServiceMethod ACTION_ENCRYPT in main actions // executeServiceMethod ACTION_ENCRYPT_FILE in main actions
extras.putString(EXTRA_TEXT, sharedText); extras.putString(EXTRA_TEXT, sharedText);
extras.putBoolean(EXTRA_ASCII_ARMOR, true); extras.putBoolean(EXTRA_ASCII_ARMOR, true);
action = ACTION_ENCRYPT; action = ACTION_ENCRYPT_FILE;
} }
} else {
} else */ {
// Files via content provider, override uri and action // Files via content provider, override uri and action
uris.clear(); uris.clear();
uris.add(intent.<Uri>getParcelableExtra(Intent.EXTRA_STREAM)); uris.add(intent.<Uri>getParcelableExtra(Intent.EXTRA_STREAM));
action = ACTION_ENCRYPT;
} }
} }
if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) { if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
action = ACTION_ENCRYPT;
} }
if (extras.containsKey(EXTRA_ASCII_ARMOR)) { if (extras.containsKey(EXTRA_ASCII_ARMOR)) {
mUseArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true); mUseArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
} }
String textData = extras.getString(EXTRA_TEXT); // preselect keys given by intent
mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
// preselect keys given by intent // Save uris
mCurrentMode = PAGER_MODE_ASYMMETRIC; mInputUris = uris;
/**
* Main Actions
*/
if (ACTION_ENCRYPT.equals(action) && textData != null) {
// encrypt text based on given extra
mMessage = textData;
mSwitchToContent = PAGER_CONTENT_MESSAGE;
} else if (ACTION_ENCRYPT.equals(action) && uris != null && !uris.isEmpty()) {
// encrypt file based on Uri
mInputUris = uris;
mSwitchToContent = PAGER_CONTENT_FILE;
} else if (ACTION_ENCRYPT.equals(action)) {
Log.e(Constants.TAG,
"Include the extra 'text' or an Uri with setData() in your Intent!");
}
} }
} }

View File

@ -54,7 +54,7 @@ public class EncryptMessageFragment extends Fragment {
*/ */
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false); View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false);
mMessage = (TextView) view.findViewById(R.id.message); mMessage = (TextView) view.findViewById(R.id.message);
mMessage.addTextChangedListener(new TextWatcher() { mMessage.addTextChangedListener(new TextWatcher() {

View File

@ -0,0 +1,477 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
*
* 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.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.Menu;
import android.view.MenuItem;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class EncryptTextActivity extends DrawerActivity implements EncryptActivityInterface {
/* Intents */
public static final String ACTION_ENCRYPT_TEXT = Constants.INTENT_PREFIX + "ENCRYPT_TEXT";
/* EXTRA keys for input */
public static final String EXTRA_TEXT = "text";
// preselect ids, for internal use
public static final String EXTRA_SIGNATURE_KEY_ID = "signature_key_id";
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids";
// view
private int mCurrentMode = MODE_ASYMMETRIC;
// tabs
private static final int MODE_ASYMMETRIC = 0;
private static final int MODE_SYMMETRIC = 1;
// model used by fragments
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
private String mPassphrase = "";
private boolean mShareAfterEncrypt = false;
private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris;
private String mMessage = "";
public boolean isModeSymmetric() {
return MODE_SYMMETRIC == mCurrentMode;
}
@Override
public boolean isUseArmor() {
return true;
}
@Override
public long getSignatureKey() {
return mSigningKeyId;
}
@Override
public long[] getEncryptionKeys() {
return mEncryptionKeyIds;
}
@Override
public String[] getEncryptionUsers() {
return mEncryptionUserIds;
}
@Override
public void setSignatureKey(long signatureKey) {
mSigningKeyId = signatureKey;
notifyUpdate();
}
@Override
public void setEncryptionKeys(long[] encryptionKeys) {
mEncryptionKeyIds = encryptionKeys;
notifyUpdate();
}
@Override
public void setEncryptionUsers(String[] encryptionUsers) {
mEncryptionUserIds = encryptionUsers;
notifyUpdate();
}
@Override
public void setPassphrase(String passphrase) {
mPassphrase = passphrase;
}
@Override
public ArrayList<Uri> getInputUris() {
if (mInputUris == null) mInputUris = new ArrayList<Uri>();
return mInputUris;
}
@Override
public ArrayList<Uri> getOutputUris() {
if (mOutputUris == null) mOutputUris = new ArrayList<Uri>();
return mOutputUris;
}
@Override
public void setInputUris(ArrayList<Uri> uris) {
mInputUris = uris;
notifyUpdate();
}
@Override
public void setOutputUris(ArrayList<Uri> uris) {
mOutputUris = uris;
notifyUpdate();
}
@Override
public String getMessage() {
return mMessage;
}
@Override
public void setMessage(String message) {
mMessage = message;
}
@Override
public void notifyUpdate() {
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment instanceof UpdateListener) {
((UpdateListener) fragment).onNotifyUpdate();
}
}
}
@Override
public void startEncrypt(boolean share) {
mShareAfterEncrypt = share;
startEncrypt();
}
public void startEncrypt() {
if (!inputIsValid()) {
// Notify was created by inputIsValid.
return;
}
// Send all information needed to service to edit key in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle());
// Message is received after encrypting is done in KeychainIntentService
KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
if (mShareAfterEncrypt) {
// Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt(message));
} else {
// Copy to clipboard
copyToClipboard(message);
Notify.showNotify(EncryptTextActivity.this,
R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
}
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog
serviceHandler.showProgressDialog(this);
// start service with intent
startService(intent);
}
private Bundle createEncryptBundle() {
// fill values for this action
Bundle data = new Bundle();
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes());
data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
Preferences.getPreferences(this).getDefaultMessageCompression());
// Always use armor for messages
data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mPassphrase;
if (passphrase.length() == 0) {
passphrase = null;
}
data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
} else {
data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID, mSigningKeyId);
data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds);
}
return data;
}
private void copyToClipboard(Message message) {
ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
}
/**
* Create Intent Chooser but exclude OK's EncryptActivity.
* <p/>
* Put together from some stackoverflow posts...
*
* @param message
* @return
*/
private Intent sendWithChooserExcludingEncrypt(Message message) {
Intent prototype = createSendIntent(message);
String title = getString(R.string.title_share_message);
// Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3
// // fallback on Android 2.3, otherwise we would get weird results
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// return Intent.createChooser(prototype, title);
// }
//
// // prevent recursion aka Inception :P
// String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"};
//
// List<LabeledIntent> targetedShareIntents = new ArrayList<LabeledIntent>();
//
// List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(prototype, 0);
// List<ResolveInfo> resInfoListFiltered = new ArrayList<ResolveInfo>();
// if (!resInfoList.isEmpty()) {
// for (ResolveInfo resolveInfo : resInfoList) {
// // do not add blacklisted ones
// if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name))
// continue;
//
// resInfoListFiltered.add(resolveInfo);
// }
//
// if (!resInfoListFiltered.isEmpty()) {
// // sorting for nice readability
// Collections.sort(resInfoListFiltered, new Comparator<ResolveInfo>() {
// @Override
// public int compare(ResolveInfo first, ResolveInfo second) {
// String firstName = first.loadLabel(getPackageManager()).toString();
// String secondName = second.loadLabel(getPackageManager()).toString();
// return firstName.compareToIgnoreCase(secondName);
// }
// });
//
// // create the custom intent list
// for (ResolveInfo resolveInfo : resInfoListFiltered) {
// Intent targetedShareIntent = (Intent) prototype.clone();
// targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName);
// targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
//
// LabeledIntent lIntent = new LabeledIntent(targetedShareIntent,
// resolveInfo.activityInfo.packageName,
// resolveInfo.loadLabel(getPackageManager()),
// resolveInfo.activityInfo.icon);
// targetedShareIntents.add(lIntent);
// }
//
// // Create chooser with only one Intent in it
// Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title);
// // append all other Intents
// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
// return chooserIntent;
// }
//
// }
// fallback to Android's default chooser
return Intent.createChooser(prototype, title);
}
private Intent createSendIntent(Message message) {
Intent sendIntent;
sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
if (!isModeSymmetric() && mEncryptionUserIds != null) {
Set<String> users = new HashSet<String>();
for (String user : mEncryptionUserIds) {
String[] userId = KeyRing.splitUserId(user);
if (userId[1] != null) {
users.add(userId[1]);
}
}
sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
}
return sendIntent;
}
private boolean inputIsValid() {
if (mMessage == null) {
Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR);
return false;
}
if (isModeSymmetric()) {
// symmetric encryption checks
if (mPassphrase == null) {
Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
return false;
}
if (mPassphrase.isEmpty()) {
Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
return false;
}
} else {
// asymmetric encryption checks
boolean gotEncryptionKeys = (mEncryptionKeyIds != null
&& mEncryptionKeyIds.length > 0);
if (!gotEncryptionKeys && mSigningKeyId == 0) {
Notify.showNotify(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR);
return false;
}
try {
if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) {
PassphraseDialogFragment.show(this, mSigningKeyId,
new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
// restart
startEncrypt();
}
}
}
);
return false;
}
} catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e);
}
}
return true;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_text_activity);
// if called with an intent action, do not init drawer navigation
if (ACTION_ENCRYPT_TEXT.equals(getIntent().getAction())) {
// TODO: back button to key?
} else {
setupDrawerNavigation(savedInstanceState);
}
// Handle intent actions
handleActions(getIntent());
updateModeFragment();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.encrypt_file_activity, menu);
return super.onCreateOptionsMenu(menu);
}
private void updateModeFragment() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.encrypt_pager_mode,
mCurrentMode == MODE_SYMMETRIC
? new EncryptSymmetricFragment()
: new EncryptAsymmetricFragment())
.commitAllowingStateLoss();
getSupportFragmentManager().executePendingTransactions();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.isCheckable()) {
item.setChecked(!item.isChecked());
}
switch (item.getItemId()) {
case R.id.check_use_symmetric:
mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC;
updateModeFragment();
notifyUpdate();
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
/**
* Handles all actions with this intent
*
* @param intent
*/
private void handleActions(Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
// Should always be text/plain
// String type = intent.getType();
ArrayList<Uri> uris = new ArrayList<Uri>();
if (extras == null) {
extras = new Bundle();
}
if (intent.getData() != null) {
uris.add(intent.getData());
}
String textData = extras.getString(EXTRA_TEXT);
// preselect keys given by intent
mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
/**
* Main Actions
*/
if (ACTION_ENCRYPT_TEXT.equals(action) && textData != null) {
mMessage = textData;
} else if (ACTION_ENCRYPT_TEXT.equals(action)) {
Log.e(Constants.TAG, "Include the extra 'text' in your Intent!");
}
}
}

View File

@ -37,7 +37,6 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.CursorAdapter; import android.support.v4.widget.CursorAdapter;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView; import android.support.v7.widget.SearchView;
import android.text.TextUtils;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -328,9 +327,9 @@ public class KeyListFragment extends LoaderFragment
@TargetApi(Build.VERSION_CODES.HONEYCOMB) @TargetApi(Build.VERSION_CODES.HONEYCOMB)
protected void encrypt(ActionMode mode, long[] masterKeyIds) { protected void encrypt(ActionMode mode, long[] masterKeyIds) {
Intent intent = new Intent(getActivity(), EncryptActivity.class); Intent intent = new Intent(getActivity(), EncryptFileActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT); intent.setAction(EncryptFileActivity.ACTION_ENCRYPT_FILE);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds); intent.putExtra(EncryptFileActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds);
// used instead of startActivity set actionbar based on callingPackage // used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0); startActivityForResult(intent, 0);

View File

@ -23,9 +23,6 @@ import android.database.Cursor;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
@ -47,7 +44,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify; import org.sufficientlysecure.keychain.util.Notify;
@ -284,9 +280,9 @@ public class ViewKeyMainFragment extends LoaderFragment implements
.getCachedPublicKeyRing(dataUri) .getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId(); .extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId}; long[] encryptionKeyIds = new long[]{keyId};
Intent intent = new Intent(getActivity(), EncryptActivity.class); Intent intent = new Intent(getActivity(), EncryptFileActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT); intent.setAction(EncryptFileActivity.ACTION_ENCRYPT_FILE);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); intent.putExtra(EncryptFileActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
// used instead of startActivity set actionbar based on callingPackage // used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0); startActivityForResult(intent, 0);
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {

View File

@ -13,5 +13,5 @@
</android.support.v4.widget.FixedDrawerLayout> </android.support.v4.widget.FixedDrawerLayout>
<include layout="@layout/encrypt_content" /> <include layout="@layout/encrypt_text_content" />
</FrameLayout> </FrameLayout>

View File

@ -5,9 +5,9 @@
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.EncryptActivity"> tools:context=".ui.EncryptFileActivity">
<include layout="@layout/encrypt_content"/> <include layout="@layout/encrypt_file_content"/>
<include layout="@layout/drawer_list" /> <include layout="@layout/drawer_list" />

View File

@ -15,17 +15,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" /> android:orientation="vertical" />
<android.support.v4.view.ViewPager <fragment
android:id="@+id/encrypt_pager_content" android:id="@+id/certify_key_fragment"
android:name="org.sufficientlysecure.keychain.ui.EncryptFileFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent" />
<android.support.v4.view.PagerTabStrip
android:id="@+id/encrypt_pager_tab_strip_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:textColor="@color/emphasis" />
</android.support.v4.view.ViewPager>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.FixedDrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.EncryptTextActivity">
<include layout="@layout/encrypt_text_content"/>
<include layout="@layout/drawer_list" />
</android.support.v4.widget.FixedDrawerLayout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_frame"
android:layout_marginLeft="@dimen/drawer_content_padding"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/notify_area"/>
<FrameLayout
android:id="@+id/encrypt_pager_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<fragment
android:id="@+id/certify_key_fragment"
android:name="org.sufficientlysecure.keychain.ui.EncryptMessageFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -4,7 +4,8 @@
<!-- title --> <!-- title -->
<string name="title_select_recipients">Select Keys</string> <string name="title_select_recipients">Select Keys</string>
<string name="title_select_secret_key">Select Your Key</string> <string name="title_select_secret_key">Select Your Key</string>
<string name="title_encrypt">Encrypt</string> <string name="title_encrypt_text">Encrypt Text</string>
<string name="title_encrypt_files">Encrypt Files</string>
<string name="title_decrypt">Decrypt</string> <string name="title_decrypt">Decrypt</string>
<string name="title_authentication">Passphrase</string> <string name="title_authentication">Passphrase</string>
<string name="title_add_subkey">Add subkey</string> <string name="title_add_subkey">Add subkey</string>
@ -498,7 +499,8 @@
<!-- Navigation Drawer --> <!-- Navigation Drawer -->
<string name="nav_keys">Keys</string> <string name="nav_keys">Keys</string>
<string name="nav_encrypt">Encrypt</string> <string name="nav_encrypt_text">Encrypt Text</string>
<string name="nav_encrypt_files">Encrypt Files</string>
<string name="nav_decrypt">Decrypt</string> <string name="nav_decrypt">Decrypt</string>
<string name="nav_apps">Apps</string> <string name="nav_apps">Apps</string>
<string name="drawer_open">Open navigation drawer</string> <string name="drawer_open">Open navigation drawer</string>