diff options
Diffstat (limited to 'OpenPGP-Keychain/src')
12 files changed, 760 insertions, 443 deletions
| diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java index 1b504a374..4c9d553ad 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java @@ -41,9 +41,6 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements      // This is the Adapter being used to display the list's data.      RegisteredAppsAdapter mAdapter; -    // If non-null, this is the current filter the user has provided. -    String mCurFilter; -      @Override      public void onActivityCreated(Bundle savedInstanceState) {          super.onActivityCreated(savedInstanceState); @@ -75,8 +72,7 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements      }      // These are the Contacts rows that we will retrieve. -    static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID, -            ApiApps.PACKAGE_NAME }; +    static final String[] PROJECTION = new String[] { ApiApps._ID, ApiApps.PACKAGE_NAME };      public Loader<Cursor> onCreateLoader(int id, Bundle args) {          // This is called when a new Loader needs to be created. This @@ -87,7 +83,7 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements          // Now create and return a CursorLoader that will take care of          // creating a Cursor for the data being displayed. -        return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null, +        return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null,                  ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC");      } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 2995a839a..7aeb51c8f 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -1,5 +1,5 @@  /* - * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>   * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>   *   * Licensed under the Apache License, Version 2.0 (the "License"); @@ -268,7 +268,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {       *        * @param intent       */ -    @SuppressWarnings("unchecked")      private void handleActionEditKey(Intent intent) {          Bundle extras = intent.getExtras(); @@ -291,7 +290,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {                      finallyEdit(masterKeyId, masterCanSign);                  } -              }          }      } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java deleted file mode 100644 index 5c8444d80..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov - * Copyright (C) 2013 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; - -import java.util.Date; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.text.format.DateFormat; -import android.widget.TextView; - -import com.actionbarsherlock.app.SherlockActivity; - -public class KeyDetailsActivity extends SherlockActivity { -    private Uri mDataUri; - -    private PGPPublicKey mPublicKey; - -    private TextView mAlgorithm; -    private TextView mFingerint; -    private TextView mExpiry; -    private TextView mCreation; - -    @Override -    protected void onCreate(Bundle savedInstanceState) { -        super.onCreate(savedInstanceState); - -        getSupportActionBar().setDisplayHomeAsUpEnabled(true); -        getSupportActionBar().setHomeButtonEnabled(true); - -        setContentView(R.layout.key_view); - -        mFingerint = (TextView) this.findViewById(R.id.fingerprint); -        mExpiry = (TextView) this.findViewById(R.id.expiry); -        mCreation = (TextView) this.findViewById(R.id.creation); -        mAlgorithm = (TextView) this.findViewById(R.id.algorithm); - -        Intent intent = getIntent(); -        mDataUri = intent.getData(); -        if (mDataUri == null) { -            Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); -            finish(); -            return; -        } else { -            Log.d(Constants.TAG, "uri: " + mDataUri); -            loadData(mDataUri); -        } -    } - -    private void loadData(Uri dataUri) { -        PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri); -        mPublicKey = ring.getPublicKey(); - -        mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper -                .convertFingerprintToHex(mPublicKey.getFingerprint()))); -        String[] mainUserId = splitUserId(""); - -        Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey); -        if (expiryDate == null) { -            mExpiry.setText(""); -        } else { -            mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); -        } - -        mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format( -                PgpKeyHelper.getCreationDate(mPublicKey))); -        mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey)); -    } - -    private String[] splitUserId(String userId) { - -        String[] result = new String[] { "", "", "" }; -        Log.v("UserID", userId); - -        Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$"); -        Matcher matcher = withComment.matcher(userId); -        if (matcher.matches()) { -            result[0] = matcher.group(1); -            result[1] = matcher.group(2); -            result[2] = matcher.group(3); -            return result; -        } - -        Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$"); -        matcher = withoutComment.matcher(userId); -        if (matcher.matches()) { -            result[0] = matcher.group(1); -            result[1] = matcher.group(2); -            return result; -        } -        return result; -    } -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java index 7b844fe08..4d1849029 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -1,18 +1,18 @@  /* - * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>   * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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.   * - *      http://www.apache.org/licenses/LICENSE-2.0 + * 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.   * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java deleted file mode 100644 index 0d61b1108..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2012-2013 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; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.widget.ExpandableListFragment; -import org.sufficientlysecure.keychain.R; - -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.View; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListFragment extends ExpandableListFragment { -    protected KeyListActivity mKeyListActivity; - -    @Override -    public void onActivityCreated(Bundle savedInstanceState) { -        super.onActivityCreated(savedInstanceState); - -        mKeyListActivity = (KeyListActivity) getActivity(); - -        // register long press context menu -        registerForContextMenu(getListView()); - -        // Give some text to display if there is no data. In a real -        // application this would come from a resource. -        setEmptyText(getString(R.string.list_empty)); -    } - -    /** -     * Context Menu on Long Click -     */ -    @Override -    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { -        super.onCreateContextMenu(menu, v, menuInfo); -        menu.add(0, Id.menu.export, 5, R.string.menu_export_key); -        menu.add(0, Id.menu.delete, 111, R.string.menu_delete_key); -    } - -    @Override -    public boolean onContextItemSelected(android.view.MenuItem item) { -        ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - -        // expInfo.id would also return row id of childs, but we always want to get the row id of -        // the group item, thus we are using the following way -        int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); -        long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - -        switch (item.getItemId()) { -        case Id.menu.export: -            long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); -            if (masterKeyId == -1) { -                masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId); -            } - -            mKeyListActivity.showExportKeysDialog(masterKeyId); -            return true; - -        case Id.menu.delete: -            mKeyListActivity.showDeleteKeyDialog(keyRingRowId); -            return true; - -        default: -            return super.onContextItemSelected(item); - -        } -    } - -} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java index 0cf15a451..3dafc84ca 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java @@ -1,5 +1,5 @@  /* - * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2013 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 @@ -17,37 +17,32 @@  package org.sufficientlysecure.keychain.ui; -import org.spongycastle.openpgp.PGPPublicKeyRing;  import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.service.remote.AppSettingsActivity; -import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter; -import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter; -import android.content.ContentUris;  import android.content.Intent;  import android.database.Cursor;  import android.net.Uri;  import android.os.Bundle; +import android.support.v4.app.LoaderManager;  import android.support.v4.content.CursorLoader;  import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.ContextMenu;  import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; + +import com.actionbarsherlock.app.SherlockListFragment; -public class KeyListPublicFragment extends KeyListFragment implements +public class KeyListPublicFragment extends SherlockListFragment implements          LoaderManager.LoaderCallbacks<Cursor> {      private KeyListPublicActivity mKeyListPublicActivity; -    private KeyListAdapter mAdapter; +    private KeyListPublicAdapter mAdapter;      /**       * Define Adapter and Loader on create of Activity @@ -58,135 +53,34 @@ public class KeyListPublicFragment extends KeyListFragment implements          mKeyListPublicActivity = (KeyListPublicActivity) getActivity(); -        mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key); -        setListAdapter(mAdapter); - -        // Start out with a progress indicator. -        setListShown(false); - -        // Prepare the loader. Either re-connect with an existing one, -        // or start a new one. -        // id is -1 as the child cursors are numbered 0,...,n -        getLoaderManager().initLoader(-1, null, this); -    } - -    /** -     * Context Menu on Long Click -     */ -    @Override -    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { -        super.onCreateContextMenu(menu, v, menuInfo); -        menu.add(0, 23, 1, R.string.title_key_details); // :TODO: Fix magic number -        menu.add(0, Id.menu.update, 2, R.string.menu_update_key); -        menu.add(0, Id.menu.signKey, 3, R.string.menu_sign_key); -        menu.add(0, Id.menu.exportToServer, 4, R.string.menu_export_key_to_server); -        menu.add(0, Id.menu.share, 6, R.string.menu_share); -        menu.add(0, Id.menu.share_qr_code, 7, R.string.menu_share_qr_code); -        menu.add(0, Id.menu.share_nfc, 8, R.string.menu_share_nfc); - -    } - -    @Override -    public boolean onContextItemSelected(android.view.MenuItem item) { -        ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - -        // expInfo.id would also return row id of childs, but we always want to get the row id of -        // the group item, thus we are using the following way -        int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); -        long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - -        switch (item.getItemId()) { -        case Id.menu.update: -            long updateKeyId = 0; -            PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId( -                    mKeyListActivity, keyRingRowId); -            if (updateKeyRing != null) { -                updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID(); +        getListView().setOnItemClickListener(new OnItemClickListener() { +            @Override +            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { +                // start key view on click +                Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class); +                detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long +                        .toString(id))); +                startActivity(detailsIntent);              } -            if (updateKeyId == 0) { -                // this shouldn't happen -                return true; -            } - -            Intent queryIntent = new Intent(mKeyListActivity, KeyServerQueryActivity.class); -            queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN); -            queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId); +        }); -            // TODO: lookup?? -            startActivityForResult(queryIntent, Id.request.look_up_key_id); +        // Give some text to display if there is no data. In a real +        // application this would come from a resource. +        setEmptyText(getString(R.string.list_empty)); -            return true; -        case 23: +        // We have a menu item to show in action bar. +        setHasOptionsMenu(true); -            Intent detailsIntent = new Intent(mKeyListActivity, KeyDetailsActivity.class); -            detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long -                    .toString(keyRingRowId))); -            startActivity(detailsIntent); -            return true; - -        case Id.menu.exportToServer: -            Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class); -            uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER); -            uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId); -            startActivityForResult(uploadIntent, Id.request.export_to_server); - -            return true; - -        case Id.menu.signKey: -            long keyId = 0; -            PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId( -                    mKeyListActivity, keyRingRowId); -            if (signKeyRing != null) { -                keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID(); -            } -            if (keyId == 0) { -                // this shouldn't happen -                return true; -            } - -            Intent signIntent = new Intent(mKeyListActivity, SignKeyActivity.class); -            signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId); -            startActivity(signIntent); - -            return true; - -        case Id.menu.share_qr_code: -            // get master key id using row id -            long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - -            Intent qrCodeIntent = new Intent(mKeyListActivity, ShareActivity.class); -            qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE); -            qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId); -            startActivityForResult(qrCodeIntent, 0); - -            return true; - -        case Id.menu.share_nfc: -            // get master key id using row id -            long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - -            Intent nfcIntent = new Intent(mKeyListActivity, ShareNfcBeamActivity.class); -            nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC); -            nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2); -            startActivityForResult(nfcIntent, 0); - -            return true; - -        case Id.menu.share: -            // get master key id using row id -            long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - -            Intent shareIntent = new Intent(mKeyListActivity, ShareActivity.class); -            shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING); -            shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3); -            startActivityForResult(shareIntent, 0); - -            return true; +        // Start out with a progress indicator. +        setListShown(false); -        default: -            return super.onContextItemSelected(item); +        // Create an empty adapter we will use to display the loaded data. +        mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key); +        setListAdapter(mAdapter); -        } +        // Prepare the loader. Either re-connect with an existing one, +        // or start a new one. +        getLoaderManager().initLoader(0, null, this);      }      // These are the rows that we will retrieve. @@ -210,7 +104,7 @@ public class KeyListPublicFragment extends KeyListFragment implements      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.setGroupCursor(data); +        mAdapter.swapCursor(data);          // The list should now be shown.          if (isResumed()) { @@ -225,7 +119,7 @@ public class KeyListPublicFragment extends KeyListFragment implements          // 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.setGroupCursor(null); +        mAdapter.swapCursor(null);      }  } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java index 822c73872..d98b5ab37 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java @@ -1,18 +1,18 @@  /* - * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>   * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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.   * - *      http://www.apache.org/licenses/LICENSE-2.0 + * 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.   * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java index 4bbf3d6ef..e8a306063 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java @@ -1,18 +1,18 @@  /* - * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>   * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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.   * - *      http://www.apache.org/licenses/LICENSE-2.0 + * 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.   * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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; @@ -22,7 +22,7 @@ import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;  import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter; +import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;  import android.database.Cursor;  import android.net.Uri; @@ -30,18 +30,18 @@ import android.os.Bundle;  import android.support.v4.app.LoaderManager;  import android.support.v4.content.CursorLoader;  import android.support.v4.content.Loader; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo;  import android.view.View; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; -public class KeyListSecretFragment extends KeyListFragment implements +import com.actionbarsherlock.app.SherlockListFragment; + +public class KeyListSecretFragment extends SherlockListFragment implements          LoaderManager.LoaderCallbacks<Cursor> {      private KeyListSecretActivity mKeyListSecretActivity; -    private KeyListAdapter mAdapter; +    private KeyListSecretAdapter mAdapter;      /**       * Define Adapter and Loader on create of Activity @@ -52,53 +52,38 @@ public class KeyListSecretFragment extends KeyListFragment implements          mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); -        mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key); -        setListAdapter(mAdapter); +        getListView().setOnItemClickListener(new OnItemClickListener() { +            @Override +            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { +                // TODO: currently this is EDIT directly, later first show VIEW -        // Start out with a progress indicator. -        setListShown(false); +                // get master key id using row id +                long masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, id); -        // Prepare the loader. Either re-connect with an existing one, -        // or start a new one. -        // id is -1 as the child cursors are numbered 0,...,n -        getLoaderManager().initLoader(-1, null, this); -    } +                boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign( +                        mKeyListSecretActivity, id); -    /** -     * Context Menu on Long Click -     */ -    @Override -    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { -        super.onCreateContextMenu(menu, v, menuInfo); -        menu.add(0, Id.menu.edit, 0, R.string.menu_edit_key); -    } - -    @Override -    public boolean onContextItemSelected(android.view.MenuItem item) { -        ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); +                mKeyListSecretActivity.editKey(masterKeyId, masterCanSign); +            } +        }); -        // expInfo.id would also return row id of childs, but we always want to get the row id of -        // the group item, thus we are using the following way -        int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); -        long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); +        // Give some text to display if there is no data. In a real +        // application this would come from a resource. +        setEmptyText(getString(R.string.list_empty)); -        // get master key id using row id -        long masterKeyId = ProviderHelper -                .getSecretMasterKeyId(mKeyListSecretActivity, keyRingRowId); +        // We have a menu item to show in action bar. +        setHasOptionsMenu(true); -        boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(mKeyListSecretActivity, -                keyRingRowId); - -        switch (item.getItemId()) { -        case Id.menu.edit: -            mKeyListSecretActivity.editKey(masterKeyId, masterCanSign); - -            return true; +        // Start out with a progress indicator. +        setListShown(false); -        default: -            return super.onContextItemSelected(item); +        // Create an empty adapter we will use to display the loaded data. +        mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, Id.type.secret_key); +        setListAdapter(mAdapter); -        } +        // Prepare the loader. Either re-connect with an existing one, +        // or start a new one. +        getLoaderManager().initLoader(0, null, this);      }      // These are the rows that we will retrieve. @@ -107,22 +92,23 @@ public class KeyListSecretFragment extends KeyListFragment implements      static final String SORT_ORDER = UserIds.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. +        // First, pick the base URI to use depending on whether we are +        // currently filtering.          Uri baseUri = KeyRings.buildSecretKeyRingsUri();          // Now create and return a CursorLoader that will take care of          // creating a Cursor for the data being displayed. -        return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); +        return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, UserIds.USER_ID +                + " COLLATE LOCALIZED ASC");      } -    @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.setGroupCursor(data); +        mAdapter.swapCursor(data);          // The list should now be shown.          if (isResumed()) { @@ -132,12 +118,10 @@ public class KeyListSecretFragment extends KeyListFragment implements          }      } -    @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.setGroupCursor(null); +        mAdapter.swapCursor(null);      } -  } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java new file mode 100644 index 000000000..c2ef64d51 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov + * Copyright (C) 2013 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; + +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract.DataUsageFeedback; +import android.text.format.DateFormat; +import android.widget.TextView; + +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +public class KeyViewActivity extends SherlockActivity { +    private Uri mDataUri; + +    private PGPPublicKey mPublicKey; + +    private TextView mAlgorithm; +    private TextView mFingerint; +    private TextView mExpiry; +    private TextView mCreation; + +    @Override +    protected void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        getSupportActionBar().setDisplayHomeAsUpEnabled(true); +        getSupportActionBar().setHomeButtonEnabled(true); + +        setContentView(R.layout.key_view_activity); + +        mFingerint = (TextView) this.findViewById(R.id.fingerprint); +        mExpiry = (TextView) this.findViewById(R.id.expiry); +        mCreation = (TextView) this.findViewById(R.id.creation); +        mAlgorithm = (TextView) this.findViewById(R.id.algorithm); + +        Intent intent = getIntent(); +        mDataUri = intent.getData(); +        if (mDataUri == null) { +            Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); +            finish(); +            return; +        } else { +            Log.d(Constants.TAG, "uri: " + mDataUri); +            loadData(mDataUri); +        } +    } + +    @Override +    public boolean onCreateOptionsMenu(Menu menu) { +        super.onCreateOptionsMenu(menu); +        getSupportMenuInflater().inflate(R.menu.key_view, menu); +        return true; +    } + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        // TODO: use data uri in the other activities instead of givin key ring row id! + +        long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment()); + +        switch (item.getItemId()) { +        case R.id.menu_key_view_update: +            long updateKeyId = 0; +            PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this, +                    keyRingRowId); +            if (updateKeyRing != null) { +                updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID(); +            } +            if (updateKeyId == 0) { +                // this shouldn't happen +                return true; +            } + +            Intent queryIntent = new Intent(this, KeyServerQueryActivity.class); +            queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN); +            queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId); + +            // TODO: lookup?? +            startActivityForResult(queryIntent, Id.request.look_up_key_id); + +            return true; +        case R.id.menu_key_view_sign: +            long keyId = 0; +            PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this, +                    keyRingRowId); +            if (signKeyRing != null) { +                keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID(); +            } +            if (keyId == 0) { +                // this shouldn't happen +                return true; +            } + +            Intent signIntent = new Intent(this, SignKeyActivity.class); +            signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId); +            startActivity(signIntent); +            return true; +        case R.id.menu_key_view_export_keyserver: +            Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class); +            uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER); +            uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId); +            startActivityForResult(uploadIntent, Id.request.export_to_server); + +            return true; +        case R.id.menu_key_view_export_file: +//            long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); +//            if (masterKeyId == -1) { +//                masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId); +//            } +// +//            mKeyListActivity.showExportKeysDialog(masterKeyId); +            return true; +        case R.id.menu_key_view_share: +            // get master key id using row id +            long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId); + +            Intent shareIntent = new Intent(this, ShareActivity.class); +            shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING); +            shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3); +            startActivityForResult(shareIntent, 0); + +            return true; +        case R.id.menu_key_view_share_qr_code: +            // get master key id using row id +            long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId); + +            Intent qrCodeIntent = new Intent(this, ShareActivity.class); +            qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE); +            qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId); +            startActivityForResult(qrCodeIntent, 0); + +            return true; +        case R.id.menu_key_view_share_nfc: +            // get master key id using row id +            long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId); + +            Intent nfcIntent = new Intent(this, ShareNfcBeamActivity.class); +            nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC); +            nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2); +            startActivityForResult(nfcIntent, 0); + +            return true; +        case R.id.menu_key_view_delete: +//            mKeyListActivity.showDeleteKeyDialog(keyRingRowId); + +            return true; + +        } +        return super.onOptionsItemSelected(item); +    } + +    private void loadData(Uri dataUri) { +        PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri); +        mPublicKey = ring.getPublicKey(); + +        mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper +                .convertFingerprintToHex(mPublicKey.getFingerprint()))); +        String[] mainUserId = splitUserId(""); + +        Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey); +        if (expiryDate == null) { +            mExpiry.setText(""); +        } else { +            mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate)); +        } + +        mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format( +                PgpKeyHelper.getCreationDate(mPublicKey))); +        mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey)); +    } + +    private String[] splitUserId(String userId) { + +        String[] result = new String[] { "", "", "" }; +        Log.v("UserID", userId); + +        Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$"); +        Matcher matcher = withComment.matcher(userId); +        if (matcher.matches()) { +            result[0] = matcher.group(1); +            result[1] = matcher.group(2); +            result[2] = matcher.group(3); +            return result; +        } + +        Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$"); +        matcher = withoutComment.matcher(userId); +        if (matcher.matches()) { +            result[0] = matcher.group(1); +            result[1] = matcher.group(2); +            return result; +        } +        return result; +    } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java new file mode 100644 index 000000000..d72c9d42a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 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.adapter; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.util.SectionCursorAdapter; + +import android.content.Context; +import android.database.Cursor; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class KeyListPublicAdapter extends SectionCursorAdapter { + +    private LayoutInflater mInflater; + +    public KeyListPublicAdapter(Context context, Cursor c, int flags) { +        super(context, c, android.R.layout.preference_category, 2); // TODO: 2 is user id + +        mInflater = LayoutInflater.from(context); +    } + +    @Override +    public void bindView(View view, Context context, Cursor cursor) { +        int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID); + +        TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); +        mainUserId.setText(R.string.unknown_user_id); +        TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); +        mainUserIdRest.setText(""); + +        String userId = cursor.getString(userIdIndex); +        if (userId != null) { +            String[] userIdSplit = OtherHelper.splitUserId(userId); + +            if (userIdSplit[1] != null) { +                mainUserIdRest.setText(userIdSplit[1]); +            } +            mainUserId.setText(userIdSplit[0]); +        } + +        if (mainUserId.getText().length() == 0) { +            mainUserId.setText(R.string.unknown_user_id); +        } + +        if (mainUserIdRest.getText().length() == 0) { +            mainUserIdRest.setVisibility(View.GONE); +        } else { +            mainUserIdRest.setVisibility(View.VISIBLE); +        } +    } + +    @Override +    public View newView(Context context, Cursor cursor, ViewGroup parent) { +        return mInflater.inflate(R.layout.key_list_group_item, null); +    } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java new file mode 100644 index 000000000..6f3129e4f --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 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.adapter; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +public class KeyListSecretAdapter extends CursorAdapter { + +    private LayoutInflater mInflater; + +    public KeyListSecretAdapter(Context context, Cursor c, int flags) { +        super(context, c, flags); + +        mInflater = LayoutInflater.from(context); +    } + +    @Override +    public void bindView(View view, Context context, Cursor cursor) { +        int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID); + +        TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); +        mainUserId.setText(R.string.unknown_user_id); +        TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); +        mainUserIdRest.setText(""); + +        String userId = cursor.getString(userIdIndex); +        if (userId != null) { +            String[] userIdSplit = OtherHelper.splitUserId(userId); + +            if (userIdSplit[1] != null) { +                mainUserIdRest.setText(userIdSplit[1]); +            } +            mainUserId.setText(userIdSplit[0]); +        } + +        if (mainUserId.getText().length() == 0) { +            mainUserId.setText(R.string.unknown_user_id); +        } + +        if (mainUserIdRest.getText().length() == 0) { +            mainUserIdRest.setVisibility(View.GONE); +        } else { +            mainUserIdRest.setVisibility(View.VISIBLE); +        } +    } + +    @Override +    public View newView(Context context, Cursor cursor, ViewGroup parent) { +        return mInflater.inflate(R.layout.key_list_group_item, null); +    } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java new file mode 100644 index 000000000..0d4092d9e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2011 Gonçalo Ferreira + *  + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.util; + +import java.util.LinkedHashMap; + +import android.content.Context; +import android.database.Cursor; +import android.database.DataSetObserver; +import android.support.v4.widget.CursorAdapter; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * Originally from https://github.com/monxalo/android-section-adapter + *  + * getCustomGroup() has been modified + */ +public abstract class SectionCursorAdapter extends CursorAdapter { + +    private static final String TAG = "SectionCursorAdapter"; +    private static final boolean LOGV = false; + +    private static final int TYPE_NORMAL = 1; +    private static final int TYPE_HEADER = 0; +    private static final int TYPE_COUNT = 2; + +    private final int mHeaderRes; +    private final int mGroupColumn; +    private final LayoutInflater mLayoutInflater; + +    private LinkedHashMap<Integer, String> sectionsIndexer; + +    public static class ViewHolder { +        public TextView textView; +    } + +    public SectionCursorAdapter(Context context, Cursor c, int headerLayout, int groupColumn) { +        super(context, c, 0); + +        sectionsIndexer = new LinkedHashMap<Integer, String>(); + +        mHeaderRes = headerLayout; +        mGroupColumn = groupColumn; +        mLayoutInflater = LayoutInflater.from(context); + +        if (c != null) { +            calculateSectionHeaders(); +            c.registerDataSetObserver(mDataSetObserver); +        } +    } + +    private DataSetObserver mDataSetObserver = new DataSetObserver() { +        public void onChanged() { +            calculateSectionHeaders(); +        }; + +        public void onInvalidated() { +            sectionsIndexer.clear(); +        }; +    }; + +    /** +     * <p> +     * This method serve as an intercepter before the sections are calculated so you can transform +     * some computer data into human readable, e.g. format a unix timestamp, or a status. +     * </p> +     *  +     * <p> +     * By default this method returns the original data for the group column. +     * </p> +     *  +     * @param groupData +     * @return +     */ +    protected String getCustomGroup(String groupData) { +        return groupData.substring(0, 1); +    } + +    private void calculateSectionHeaders() { +        int i = 0; + +        String previous = ""; +        int count = 0; + +        final Cursor c = getCursor(); + +        sectionsIndexer.clear(); + +        if (c == null) { +            return; +        } + +        c.moveToPosition(-1); + +        while (c.moveToNext()) { +            final String group = getCustomGroup(c.getString(mGroupColumn)); + +            if (!previous.equals(group)) { +                sectionsIndexer.put(i + count, group); +                previous = group; + +                if (LOGV) +                    Log.v(TAG, "Group " + group + "at position: " + (i + count)); + +                count++; +            } + +            i++; +        } +    } + +    public String getGroupCustomFormat(Object obj) { +        return null; +    } + +    @Override +    public View getView(int position, View convertView, ViewGroup parent) { +        int viewType = getItemViewType(position); + +        if (viewType == TYPE_NORMAL) { +            Cursor c = (Cursor) getItem(position); + +            if (c == null) { +                if (LOGV) +                    Log.d(TAG, "getItem(" + position + ") = null"); +                return mLayoutInflater.inflate(mHeaderRes, parent, false); +            } + +            final int mapCursorPos = getSectionForPosition(position); +            c.moveToPosition(mapCursorPos); + +            return super.getView(mapCursorPos, convertView, parent); +        } else { +            ViewHolder holder = null; + +            if (convertView == null) { +                if (LOGV) +                    Log.v(TAG, "Creating new view for section"); + +                holder = new ViewHolder(); +                convertView = mLayoutInflater.inflate(mHeaderRes, parent, false); +                holder.textView = (TextView) convertView; + +                convertView.setTag(holder); +            } else { +                if (LOGV) +                    Log.v(TAG, "Reusing view for section"); + +                holder = (ViewHolder) convertView.getTag(); +            } + +            TextView sectionText = holder.textView; + +            final String group = sectionsIndexer.get(position); +            final String customFormat = getGroupCustomFormat(group); + +            sectionText.setText(customFormat == null ? group : customFormat); + +            return sectionText; +        } +    } + +    @Override +    public int getViewTypeCount() { +        return TYPE_COUNT; +    } + +    @Override +    public int getCount() { +        return super.getCount() + sectionsIndexer.size(); +    } + +    @Override +    public boolean isEnabled(int position) { +        return getItemViewType(position) == TYPE_NORMAL; +    } + +    public int getPositionForSection(int section) { +        if (sectionsIndexer.containsKey(section)) { +            return section + 1; +        } +        return section; +    } + +    public int getSectionForPosition(int position) { +        int offset = 0; +        for (Integer key : sectionsIndexer.keySet()) { +            if (position > key) { +                offset++; +            } else { +                break; +            } +        } + +        return position - offset; +    } + +    @Override +    public Object getItem(int position) { +        if (getItemViewType(position) == TYPE_NORMAL) { +            return super.getItem(getSectionForPosition(position)); +        } +        return super.getItem(position); +    } + +    @Override +    public long getItemId(int position) { +        if (getItemViewType(position) == TYPE_NORMAL) { +            return super.getItemId(getSectionForPosition(position)); +        } +        return super.getItemId(position); +    } + +    @Override +    public int getItemViewType(int position) { +        if (position == getPositionForSection(position)) { +            return TYPE_NORMAL; +        } +        return TYPE_HEADER; +    } + +    @Override +    public void changeCursor(Cursor cursor) { +        final Cursor old = swapCursor(cursor); + +        if (old != null) { +            old.close(); +        } +    } + +    @Override +    public Cursor swapCursor(Cursor newCursor) { +        if (getCursor() != null) { +            getCursor().unregisterDataSetObserver(mDataSetObserver); +        } + +        final Cursor oldCursor = super.swapCursor(newCursor); + +        calculateSectionHeaders(); + +        if (newCursor != null) { +            newCursor.registerDataSetObserver(mDataSetObserver); +        } + +        return oldCursor; +    } +}
\ No newline at end of file | 
