aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/brcmImage.pl
blob: 9a3acb43a4dbad1b32d0b58b6857e29c915fd9e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/perl
#
#    Copyright (C) 2009	Henk Vergonet <Henk.Vergonet@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 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, write to the Free Software
#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

# Description:
#   Replacement for brcmImagebuilder
#
# Disclaimer:
#   Use this software at your own risk.
#
# Changelog:
#   2009-01-01	Henk.Vergonet at gmail.com
#
use strict;
use Getopt::Std;
use Compress::Zlib;

my $version = "0.1";
my %arg = (
	o => 'bcm963xx_fs_kernel',
	b => 'OpenWrt',
	c => '6348',
	s => 64,
	f => 0xbfc00000,
	x => 0x00010000,
	a => 0x80010000,
	e => 0x80010000,
	i => 2,
);
my $prog = $0;
$prog =~ s/^.*\///;
getopts("r:k:o:lc:b:s:f:i:a:e:tpvh", \%arg);

die "usage: $prog ~opts~

  -r <file>	: input rootfs file
  -k <file>	: input kernel file
  -o <file>	: output image file, default $arg{o}
  -l		: littleendian system, default ".($arg{l} ? 'yes' : 'no')."
  -c <chipid>	: default $arg{c} 
  -b <boardid>	: default $arg{b} 
  -s <size_kb>	: erase sise flash, default $arg{s} 
  -f <baseaddr>	: flash base, default ".sprintf('0x%x', $arg{f})."
  -x <cfelen>	: length of cfe, default ".sprintf('0x%x', $arg{x})."
  -i		: 2=dual image, default $arg{i}

  -a <loadaddr>	: Kernel load address, default ".sprintf('0x%x', $arg{a})."
  -e <entryaddr>: Kernel entry address, default ".sprintf('0x%x', $arg{e})."
  -t		: Prefix kernel with load,entry,size

  -p		: Add a 'gOtO' partition 

  -v		: be more verbose
  -h		: help, version $version

EXAMPLES:
    $prog -k kern -r rootfs
" if $arg{h} || !$arg{k} || !$arg{r};

sub Read_Image
{
	open my $fh, $_[0] or die "open $_[0]: $!";
	local $/;	# Set input to "slurp" mode.
	my $buf = <$fh>;
	close $fh;
	return $buf;
}

sub Padlen
{
	my $p = $_[0] % $_[1];
	return ($p ? $_[1] - $p : 0);
}

sub Pad
{
	my ($buf, $off, $bs) = @_[0..2];
	$buf .= chr(255) x Padlen(length($buf) + $off, $bs);
	return $buf;
}

sub bcmImage
{
	my ($k, $f) = @_[0..1];
	my $tmp = $arg{x} + 0x100 + $arg{f};
	
	# regular: rootfs+kernel
	my ($img, $fa, $ka) = ( $f.$k, $tmp, $tmp + length($f) );

	# test: kernel+rootfs
#	my ($img, $fa, $ka) = ( $k.$f, $tmp + length($k), $tmp );

	$fa = 0 unless length($f);

	my $hdr = pack("a4a20a14a6a16a2a10a12a10a12a10a12a10a2a2a74Na16",
		'6',
		'LinuxInside', 
		'ver. 2.0', 
		$arg{c},
		$arg{b},
		($arg{l} ? '0' : '1'),
		length($img),
		'0',
		'0',
		$fa,
		length($f),
		$ka,
		length($k),
		($arg{i}==2 ? '1' : '0'),
		'',		# if 1, the image is INACTIVE; if 0, active
		'',
		~crc32($k, crc32($f)),
		'');
	$hdr .= pack('Na16', ~crc32($hdr), '');

	printf "kernel at 0x%x length 0x%x(%u)\n", $ka, length($k), length($k)
		if $arg{v};
	printf "rootfs at 0x%x length 0x%x(%u)\n", $fa, length($f), length($f)
		if $arg{v};

	open(FO, ">$arg{o}");
	print FO $hdr;
	print FO $img;
	close FO;
}

# MAIN

my $kern = Read_Image $arg{k};
my $root = Read_Image $arg{r};

$kern = pack('NNN', $arg{a}, $arg{e}, length($kern)).$kern if $arg{t};

# specific fixup for the CFE that expects rootfs-kernel order
if ($arg{p}) {
	$kern = Pad($kern, 0x10c, $arg{s} * 1024);
	my $dummy_root = pack('a4NN',
			'gOtO',
			length($kern)+12,
			length($root)+Padlen(length($root), $arg{s} * 1024)
	);
	$kern .= $root;
	$root = $dummy_root;
}

bcmImage($kern, $root);
class="nn">android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; 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.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; import com.getbase.floatingactionbutton.FloatingActionButton; import com.getbase.floatingactionbutton.FloatingActionsMenu; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.CloudImportService; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; /** * Public key list with sticky list headers. It does _not_ extend ListFragment because it uses * StickyListHeaders library which does not extend upon ListView. */ public class KeyListFragment extends LoaderFragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor>, FabContainer { static final int REQUEST_REPEAT_PASSPHRASE = 1; static final int REQUEST_ACTION = 2; ExportHelper mExportHelper; private KeyListAdapter mAdapter; private StickyListHeadersListView mStickyList; // saves the mode object for multiselect, needed for reset at some point private ActionMode mActionMode = null; private String mQuery; private FloatingActionsMenu mFab; // This ids for multiple key export. private ArrayList<Long> mIdsForRepeatAskPassphrase; // This index for remembering the number of master key. private int mIndex; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mExportHelper = new ExportHelper(getActivity()); } /** * Load custom layout with StickyListView from library */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = super.onCreateView(inflater, superContainer, savedInstanceState); View view = inflater.inflate(R.layout.key_list_fragment, getContainer()); mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list); mStickyList.setOnItemClickListener(this); mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main); FloatingActionButton fabQrCode = (FloatingActionButton) view.findViewById(R.id.fab_add_qr_code); FloatingActionButton fabCloud = (FloatingActionButton) view.findViewById(R.id.fab_add_cloud); FloatingActionButton fabFile = (FloatingActionButton) view.findViewById(R.id.fab_add_file); fabQrCode.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mFab.collapse(); scanQrCode(); } }); fabCloud.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mFab.collapse(); searchCloud(); } }); fabFile.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mFab.collapse(); importFile(); } }); return root; } /** * Define Adapter and Loader on create of Activity */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // show app name instead of "keys" from nav drawer getActivity().setTitle(R.string.app_name); mStickyList.setOnItemClickListener(this); mStickyList.setAreHeadersSticky(true); mStickyList.setDrawingListUnderStickyHeader(false); mStickyList.setFastScrollEnabled(true); // Adds an empty footer view so that the Floating Action Button won't block content // in last few rows. View footer = new View(getActivity()); int spacing = (int) android.util.TypedValue.applyDimension( android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics() ); android.widget.AbsListView.LayoutParams params = new android.widget.AbsListView.LayoutParams( android.widget.AbsListView.LayoutParams.MATCH_PARENT, spacing ); footer.setLayoutParams(params); mStickyList.addFooterView(footer, null, false); /* * Multi-selection */ mStickyList.setFastScrollAlwaysVisible(true); mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { android.view.MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.key_list_multi, menu); mActionMode = mode; return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // get IDs for checked positions as long array long[] ids; switch (item.getItemId()) { case R.id.menu_key_list_multi_encrypt: { ids = mAdapter.getCurrentSelectedMasterKeyIds(); encrypt(mode, ids); break; } case R.id.menu_key_list_multi_delete: { ids = mAdapter.getCurrentSelectedMasterKeyIds(); showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected()); break; } case R.id.menu_key_list_multi_export: { ids = mAdapter.getCurrentSelectedMasterKeyIds(); showMultiExportDialog(ids); break; } case R.id.menu_key_list_multi_select_all: { // select all for (int i = 0; i < mAdapter.getCount(); i++) { mStickyList.setItemChecked(i, true); } break; } } return true; } @Override public void onDestroyActionMode(ActionMode mode) { mActionMode = null; mAdapter.clearSelection(); } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { if (checked) { mAdapter.setNewSelection(position, true); } else { mAdapter.removeSelection(position); } int count = mStickyList.getCheckedItemCount(); String keysSelected = getResources().getQuantityString( R.plurals.key_list_selected_keys, count, count); mode.setTitle(keysSelected); } }); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Start out with a progress indicator. setContentShown(false); // Create an empty adapter we will use to display the loaded data. mAdapter = new KeyListAdapter(getActivity(), null, 0); mStickyList.setAdapter(mAdapter); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } static final String ORDER = KeyRings.HAS_ANY_SECRET + " DESC, UPPER(" + KeyRings.USER_ID + ") ASC"; @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); String where = null; String whereArgs[] = null; if (mQuery != null) { String[] words = mQuery.trim().split("\\s+"); whereArgs = new String[words.length]; for (int i = 0; i < words.length; ++i) { if (where == null) { where = ""; } else { where += " AND "; } where += KeyRings.USER_ID + " LIKE ?"; whereArgs[i] = "%" + words[i] + "%"; } } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. return new CursorLoader(getActivity(), baseUri, KeyListAdapter.PROJECTION, where, whereArgs, ORDER); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.setSearchQuery(mQuery); mAdapter.swapCursor(data); mStickyList.setAdapter(mAdapter); // this view is made visible if no data is available mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty)); // end action mode, if any if (mActionMode != null) { mActionMode.finish(); } // The list should now be shown. if (isResumed()) { setContentShown(true); } else { setContentShownNoAnimation(true); } } @Override public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } /** * On click on item, start key view activity */ @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class); viewIntent.setData( KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position))); startActivity(viewIntent); } protected void encrypt(ActionMode mode, long[] masterKeyIds) { Intent intent = new Intent(getActivity(), EncryptFilesActivity.class); intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds); // used instead of startActivity set actionbar based on callingPackage startActivityForResult(intent, REQUEST_ACTION); mode.finish(); } /** * Show dialog to delete key * * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not */ public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { // Can only work on singular secret keys if (hasSecret && masterKeyIds.length > 1) { Notify.create(getActivity(), R.string.secret_cannot_multiple, Notify.Style.ERROR).show(); return; } // Message is received after key is deleted Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { if (message.arg1 == DeleteKeyDialogFragment.MESSAGE_OKAY) { Bundle data = message.getData(); if (data != null) { DeleteResult result = data.getParcelable(DeleteResult.EXTRA_RESULT); if (result != null) { result.createNotify(getActivity()).show(); } } mode.finish(); } } }; // Create a new Messenger for the communication back Messenger messenger = new Messenger(returnHandler); DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, masterKeyIds); deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); } @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { inflater.inflate(R.menu.key_list, menu); if (Constants.DEBUG) { menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true); menu.findItem(R.id.menu_key_list_debug_read).setVisible(true); menu.findItem(R.id.menu_key_list_debug_write).setVisible(true); menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true); } // Get the searchview MenuItem searchItem = menu.findItem(R.id.menu_key_list_search); SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); // Execute this when searching searchView.setOnQueryTextListener(this); // Erase search result without focus MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { // disable swipe-to-refresh // mSwipeRefreshLayout.setIsLocked(true); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { mQuery = null; getLoaderManager().restartLoader(0, null, KeyListFragment.this); // enable swipe-to-refresh // mSwipeRefreshLayout.setIsLocked(false); return true; } }); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_key_list_create: createKey(); return true; case R.id.menu_key_list_export: mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); return true; case R.id.menu_key_list_update_all_keys: updateAllKeys(); return true; case R.id.menu_key_list_debug_cons: consolidate(); return true; case R.id.menu_key_list_debug_read: try { KeychainDatabase.debugBackup(getActivity(), true); Notify.create(getActivity(), "Restored debug_backup.db", Notify.Style.OK).show(); getActivity().getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null); } catch (IOException e) { Log.e(Constants.TAG, "IO Error", e); Notify.create(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR).show(); } return true; case R.id.menu_key_list_debug_write: try { KeychainDatabase.debugBackup(getActivity(), false); Notify.create(getActivity(), "Backup to debug_backup.db completed", Notify.Style.OK).show(); } catch (IOException e) { Log.e(Constants.TAG, "IO Error", e); Notify.create(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR).show(); } return true; case R.id.menu_key_list_debug_first_time: Preferences prefs = Preferences.getPreferences(getActivity()); prefs.setFirstTime(true); Intent intent = new Intent(getActivity(), CreateKeyActivity.class); intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true); startActivity(intent); getActivity().finish(); return true; default: return super.onOptionsItemSelected(item); } } @Override public boolean onQueryTextSubmit(String s) { return true; } @Override public boolean onQueryTextChange(String s) { Log.d(Constants.TAG, "onQueryTextChange s:" + s); // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. // If the nav drawer is opened, onQueryTextChange("") is executed. // This hack prevents restarting the loader. // TODO: better way to fix this? String tmp = (mQuery == null) ? "" : mQuery; if (!s.equals(tmp)) { mQuery = s; getLoaderManager().restartLoader(0, null, this); } return true; } private void searchCloud() { Intent importIntent = new Intent(getActivity(), ImportKeysActivity.class); importIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, (String) null); // hack to show only cloud tab startActivity(importIntent); } private void scanQrCode() { Intent scanQrCode = new Intent(getActivity(), ImportKeysProxyActivity.class); scanQrCode.setAction(ImportKeysProxyActivity.ACTION_SCAN_IMPORT); startActivityForResult(scanQrCode, REQUEST_ACTION); } private void importFile() { Intent intentImportExisting = new Intent(getActivity(), ImportKeysActivity.class); intentImportExisting.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); startActivityForResult(intentImportExisting, REQUEST_ACTION); } private void createKey() { Intent intent = new Intent(getActivity(), CreateKeyActivity.class); startActivityForResult(intent, REQUEST_ACTION); } private void updateAllKeys() { Context context = getActivity(); ProviderHelper providerHelper = new ProviderHelper(context); Cursor cursor = providerHelper.getContentResolver().query( KeyRings.buildUnifiedKeyRingsUri(), new String[]{ KeyRings.FINGERPRINT }, null, null, null ); ArrayList<ParcelableKeyRing> keyList = new ArrayList<>(); while (cursor.moveToNext()) { byte[] blob = cursor.getBlob(0);//fingerprint column is 0 String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); keyList.add(keyEntry); } ServiceProgressHandler serviceHandler = new ServiceProgressHandler( getActivity(), getString(R.string.progress_updating), ProgressDialog.STYLE_HORIZONTAL, true, ProgressDialogFragment.ServiceType.CLOUD_IMPORT) { 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 ImportKeyResult result = returnData.getParcelable(OperationResult.EXTRA_RESULT); if (result == null) { Log.e(Constants.TAG, "result == null"); return; } result.createNotify(getActivity()).show(); } } }; // Send all information needed to service to query keys in other thread Intent intent = new Intent(getActivity(), CloudImportService.class); // fill values for this action Bundle data = new Bundle(); // search config { Preferences prefs = Preferences.getPreferences(getActivity()); Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); data.putString(CloudImportService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); } data.putParcelableArrayList(CloudImportService.IMPORT_KEY_LIST, keyList); intent.putExtra(CloudImportService.EXTRA_DATA, data); // Create a new Messenger for the communication back Messenger messenger = new Messenger(serviceHandler); intent.putExtra(CloudImportService.EXTRA_MESSENGER, messenger); // show progress dialog serviceHandler.showProgressDialog(getActivity()); // start service with intent getActivity().startService(intent); } private void consolidate() { // Message is received after importing is done in KeychainIntentService ServiceProgressHandler saveHandler = new ServiceProgressHandler( getActivity(), getString(R.string.progress_importing), ProgressDialog.STYLE_HORIZONTAL, ProgressDialogFragment.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 ConsolidateResult result = returnData.getParcelable(OperationResult.EXTRA_RESULT); if (result == null) { return; } result.createNotify(getActivity()).show(); } } }; // Send all information needed to service to import key in other thread Intent intent = new Intent(getActivity(), KeychainIntentService.class); intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE); // fill values for this action Bundle data = new Bundle(); 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); } private void showMultiExportDialog(long[] masterKeyIds) { mIdsForRepeatAskPassphrase = new ArrayList<>(); for (long id : masterKeyIds) { try { if (PassphraseCacheService.getCachedPassphrase( getActivity(), id, id) == null) { mIdsForRepeatAskPassphrase.add(id); } } catch (PassphraseCacheService.KeyNotFoundException e) { // This happens when the master key is stripped // and ignore this key. } } mIndex = 0; if (mIdsForRepeatAskPassphrase.size() != 0) { startPassphraseActivity(); return; } long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) { idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i); } mExportHelper.showExportKeysDialog(idsForMultiExport, Constants.Path.APP_DIR_FILE, mAdapter.isAnySecretSelected()); } private void startPassphraseActivity() { Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++); intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, masterKeyId); startActivityForResult(intent, REQUEST_REPEAT_PASSPHRASE); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_REPEAT_PASSPHRASE) { if (resultCode != Activity.RESULT_OK) { return; } if (mIndex < mIdsForRepeatAskPassphrase.size()) { startPassphraseActivity(); return; } long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()]; for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) { idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i); } mExportHelper.showExportKeysDialog(idsForMultiExport, Constants.Path.APP_DIR_FILE, mAdapter.isAnySecretSelected()); } if (requestCode == REQUEST_ACTION) { // if a result has been returned, display a notify if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); result.createNotify(getActivity()).show(); } else { super.onActivityResult(requestCode, resultCode, data); } } } @Override public void fabMoveUp(int height) { ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0, -height); // we're a little behind, so skip 1/10 of the time anim.setDuration(270); anim.start(); } @Override public void fabRestorePosition() { ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0); // we're a little ahead, so wait a few ms anim.setStartDelay(70); anim.setDuration(300); anim.start(); } public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter { private HashMap<Integer, Boolean> mSelection = new HashMap<>(); public KeyListAdapter(Context context, Cursor c, int flags) { super(context, c, flags); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { View view = super.newView(context, cursor, parent); final KeyItemViewHolder holder = (KeyItemViewHolder) view.getTag(); holder.mSlinger.setVisibility(View.VISIBLE); holder.mSlingerButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (holder.mMasterKeyId != null) { Intent safeSlingerIntent = new Intent(mContext, SafeSlingerActivity.class); safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, holder.mMasterKeyId); startActivityForResult(safeSlingerIntent, REQUEST_ACTION); } } }); return view; } @Override public View getView(int position, View convertView, ViewGroup parent) { // let the adapter handle setting up the row views View v = super.getView(position, convertView, parent); if (mSelection.get(position) != null) { // selected position color v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); } else { // default color v.setBackgroundColor(Color.TRANSPARENT); } return v; } private class HeaderViewHolder { TextView mText; TextView mCount; } /** * Creates a new header view and binds the section headers to it. It uses the ViewHolder * pattern. Most functionality is similar to getView() from Android's CursorAdapter. * <p/> * NOTE: The variables mDataValid and mCursor are available due to the super class * CursorAdapter. */ @Override public View getHeaderView(int position, View convertView, ViewGroup parent) { HeaderViewHolder holder; if (convertView == null) { holder = new HeaderViewHolder(); convertView = mInflater.inflate(R.layout.key_list_header, parent, false); holder.mText = (TextView) convertView.findViewById(R.id.stickylist_header_text); holder.mCount = (TextView) convertView.findViewById(R.id.contacts_num); convertView.setTag(holder); } else { holder = (HeaderViewHolder) convertView.getTag(); } if (!mDataValid) { // no data available at this point Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); return convertView; } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException("couldn't move cursor to position " + position); } if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) { { // set contact count int num = mCursor.getCount(); String contactsTotal = mContext.getResources().getQuantityString(R.plurals.n_keys, num, num); holder.mCount.setText(contactsTotal); holder.mCount.setVisibility(View.VISIBLE); } holder.mText.setText(convertView.getResources().getString(R.string.my_keys)); return convertView; } // set header text as first char in user id String userId = mCursor.getString(INDEX_USER_ID); String headerText = convertView.getResources().getString(R.string.user_id_no_name); if (userId != null && userId.length() > 0) { headerText = "" + userId.charAt(0); } holder.mText.setText(headerText); holder.mCount.setVisibility(View.GONE); return convertView; } /** * Header IDs should be static, position=1 should always return the same Id that is. */ @Override public long getHeaderId(int position) { if (!mDataValid) { // no data available at this point Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); return -1; } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException("couldn't move cursor to position " + position); } // early breakout: all secret keys are assigned id 0 if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) { return 1L; } // otherwise, return the first character of the name as ID String userId = mCursor.getString(INDEX_USER_ID); if (userId != null && userId.length() > 0) { return Character.toUpperCase(userId.charAt(0)); } else { return Long.MAX_VALUE; } } /** * -------------------------- MULTI-SELECTION METHODS -------------- */ public void setNewSelection(int position, boolean value) { mSelection.put(position, value); notifyDataSetChanged(); } public boolean isAnySecretSelected() { for (int pos : mSelection.keySet()) { if (isSecretAvailable(pos)) return true; } return false; } public long[] getCurrentSelectedMasterKeyIds() { long[] ids = new long[mSelection.size()]; int i = 0; // get master key ids for (int pos : mSelection.keySet()) { ids[i++] = getMasterKeyId(pos); } return ids; } public void removeSelection(int position) { mSelection.remove(position); notifyDataSetChanged(); } public void clearSelection() { mSelection.clear(); notifyDataSetChanged(); } } }