| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php |
| * |
| * 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 com.android.ide.eclipse.adt.internal.preferences; |
| |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; |
| import com.android.ide.eclipse.adt.internal.utils.FingerprintUtils; |
| import com.android.prefs.AndroidLocation.AndroidLocationException; |
| import com.android.sdklib.internal.build.DebugKeyProvider; |
| import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; |
| |
| import org.eclipse.jface.preference.BooleanFieldEditor; |
| import org.eclipse.jface.preference.FieldEditorPreferencePage; |
| import org.eclipse.jface.preference.FileFieldEditor; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.RadioGroupFieldEditor; |
| import org.eclipse.jface.preference.StringFieldEditor; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPreferencePage; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.security.GeneralSecurityException; |
| import java.security.PrivateKey; |
| import java.security.cert.Certificate; |
| import java.security.cert.X509Certificate; |
| import java.util.Date; |
| |
| /** |
| * Preference page for build options. |
| * |
| */ |
| public class BuildPreferencePage extends FieldEditorPreferencePage implements |
| IWorkbenchPreferencePage { |
| |
| private IPreferenceStore mPrefStore = null; |
| |
| // default key store |
| private ReadOnlyFieldEditor mDefaultKeyStore = null; |
| private LabelField mDefaultFingerprintMd5 = null; |
| private LabelField mDefaultFingerprintSha1 = null; |
| |
| // custom key store |
| private KeystoreFieldEditor mCustomKeyStore = null; |
| private LabelField mCustomFingerprintMd5 = null; |
| private LabelField mCustomFingerprintSha1 = null; |
| |
| public BuildPreferencePage() { |
| super(GRID); |
| mPrefStore = AdtPlugin.getDefault().getPreferenceStore(); |
| setPreferenceStore(mPrefStore); |
| setDescription(Messages.BuildPreferencePage_Title); |
| } |
| |
| @Override |
| protected void createFieldEditors() { |
| addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_RES_AUTO_REFRESH, |
| Messages.BuildPreferencePage_Auto_Refresh_Resources_on_Build, |
| getFieldEditorParent())); |
| |
| addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_FORCE_ERROR_ON_NATIVELIB_IN_JAR, |
| "Force error when external jars contain native libraries", |
| getFieldEditorParent())); |
| |
| addField(new BooleanFieldEditor(AdtPrefs.PREFS_BUILD_SKIP_POST_COMPILE_ON_FILE_SAVE, |
| "Skip packaging and dexing until export or launch. (Speeds up automatic builds on file save)", |
| getFieldEditorParent())); |
| |
| RadioGroupFieldEditor rgfe = new RadioGroupFieldEditor( |
| AdtPrefs.PREFS_BUILD_VERBOSITY, |
| Messages.BuildPreferencePage_Build_Output, 1, new String[][] { |
| { Messages.BuildPreferencePage_Silent, BuildVerbosity.ALWAYS.name() }, |
| { Messages.BuildPreferencePage_Normal, BuildVerbosity.NORMAL.name() }, |
| { Messages.BuildPreferencePage_Verbose, BuildVerbosity.VERBOSE.name() } |
| }, |
| getFieldEditorParent(), true); |
| addField(rgfe); |
| |
| // default debug keystore fingerprints |
| Fingerprints defaultFingerprints = getFingerprints( |
| mPrefStore.getString(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE)); |
| |
| // default debug key store fields |
| mDefaultKeyStore = new ReadOnlyFieldEditor(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE, |
| Messages.BuildPreferencePage_Default_KeyStore, getFieldEditorParent()); |
| mDefaultFingerprintMd5 = new LabelField( |
| Messages.BuildPreferencePage_Default_Certificate_Fingerprint_MD5, |
| defaultFingerprints != null ? defaultFingerprints.md5 : "", |
| getFieldEditorParent()); |
| mDefaultFingerprintSha1 = new LabelField( |
| Messages.BuildPreferencePage_Default_Certificate_Fingerprint_SHA1, |
| defaultFingerprints != null ? defaultFingerprints.sha1 : "", |
| getFieldEditorParent()); |
| |
| addField(mDefaultKeyStore); |
| addField(mDefaultFingerprintMd5); |
| addField(mDefaultFingerprintSha1); |
| |
| // custom debug keystore fingerprints |
| Fingerprints customFingerprints = null; |
| |
| String customDebugKeystorePath = mPrefStore.getString(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE); |
| if (new File(customDebugKeystorePath).isFile()) { |
| customFingerprints = getFingerprints(customDebugKeystorePath); |
| } else { |
| // file does not exist. |
| setErrorMessage("Not a valid keystore path."); |
| } |
| |
| // custom debug key store fields |
| mCustomKeyStore = new KeystoreFieldEditor(AdtPrefs.PREFS_CUSTOM_DEBUG_KEYSTORE, |
| Messages.BuildPreferencePage_Custom_Keystore, getFieldEditorParent()); |
| mCustomFingerprintMd5 = new LabelField( |
| Messages.BuildPreferencePage_Default_Certificate_Fingerprint_MD5, |
| customFingerprints != null ? customFingerprints.md5 : "", |
| getFieldEditorParent()); |
| mCustomFingerprintSha1 = new LabelField( |
| Messages.BuildPreferencePage_Default_Certificate_Fingerprint_SHA1, |
| customFingerprints != null ? customFingerprints.sha1 : "", |
| getFieldEditorParent()); |
| |
| // set fingerprint fields |
| mCustomKeyStore.setFingerprintMd5Field(mCustomFingerprintMd5); |
| mCustomKeyStore.setFingerprintSha1Field(mCustomFingerprintSha1); |
| |
| addField(mCustomKeyStore); |
| addField(mCustomFingerprintMd5); |
| addField(mCustomFingerprintSha1); |
| } |
| |
| /** |
| * MD5 & SHA1 fingerprints. |
| */ |
| private static class Fingerprints { |
| final String md5; |
| final String sha1; |
| |
| Fingerprints(String md5Val, String sha1Val) { |
| md5 = md5Val; |
| sha1 = sha1Val; |
| } |
| } |
| |
| private Fingerprints getFingerprints(String keystorePath) { |
| // attempt to load the debug key. |
| try { |
| DebugKeyProvider keyProvider = new DebugKeyProvider(keystorePath, |
| null /* storeType */, null /* key gen output */); |
| |
| return new Fingerprints( |
| FingerprintUtils.getFingerprint(keyProvider.getCertificate(), "MD5"), |
| FingerprintUtils.getFingerprint(keyProvider.getCertificate(), "SHA1")); |
| } catch (GeneralSecurityException e) { |
| setErrorMessage(e.getMessage()); |
| } catch (IOException e) { |
| setErrorMessage(e.getMessage()); |
| } catch (KeytoolException e) { |
| setErrorMessage(e.getMessage()); |
| } catch (AndroidLocationException e) { |
| setErrorMessage(e.getMessage()); |
| } |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) |
| */ |
| @Override |
| public void init(IWorkbench workbench) { |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.preference.FieldEditorPreferencePage#performDefaults |
| * (org.eclipse.jface.preference.PreferencePage#performDefaults) |
| */ |
| @Override |
| protected void performDefaults() { |
| super.performDefaults(); |
| |
| // restore the default key store fingerprints |
| Fingerprints defaultFingerprints = getFingerprints(mPrefStore |
| .getString(AdtPrefs.PREFS_DEFAULT_DEBUG_KEYSTORE)); |
| mDefaultFingerprintMd5.setStringValue(defaultFingerprints.md5); |
| mDefaultFingerprintSha1.setStringValue(defaultFingerprints.sha1); |
| |
| // set custom fingerprint fields to blank |
| mCustomFingerprintMd5.setStringValue(""); |
| mCustomFingerprintSha1.setStringValue(""); |
| } |
| |
| /** |
| * A read-only string field editor. |
| */ |
| private static class ReadOnlyFieldEditor extends StringFieldEditor { |
| |
| public ReadOnlyFieldEditor(String name, String labelText, Composite parent) { |
| super(name, labelText, parent); |
| } |
| |
| @Override |
| protected void createControl(Composite parent) { |
| super.createControl(parent); |
| |
| Text control = getTextControl(); |
| control.setEditable(false); |
| } |
| } |
| |
| /** |
| * A read-only string field. |
| */ |
| private static class LabelField extends StringFieldEditor { |
| private String text; |
| |
| public LabelField(String labelText, String value, Composite parent) { |
| super("", labelText, parent); |
| text = value; |
| } |
| |
| @Override |
| protected void createControl(Composite parent) { |
| super.createControl(parent); |
| |
| Text control = getTextControl(); |
| control.setEditable(false); |
| } |
| |
| @Override |
| protected void doLoad() { |
| setStringValue(text); |
| } |
| |
| @Override |
| protected void doStore() { |
| // Do nothing |
| } |
| } |
| |
| /** |
| * Custom {@link FileFieldEditor} that checks that the keystore is valid. |
| */ |
| private static class KeystoreFieldEditor extends FileFieldEditor { |
| private StringFieldEditor fingerprintMd5 = null; |
| private StringFieldEditor fingerprintSha1 = null; |
| |
| public void setFingerprintMd5Field(StringFieldEditor field) { |
| fingerprintMd5 = field; |
| } |
| |
| public void setFingerprintSha1Field(StringFieldEditor field) { |
| fingerprintSha1 = field; |
| } |
| |
| public KeystoreFieldEditor(String name, String label, Composite parent) { |
| super(name, label, parent); |
| setValidateStrategy(VALIDATE_ON_KEY_STROKE); |
| } |
| |
| @Override |
| protected boolean checkState() { |
| String fileName = getTextControl().getText(); |
| fileName = fileName.trim(); |
| |
| if (fingerprintMd5 != null) { |
| fingerprintMd5.setStringValue(""); |
| } |
| |
| if (fingerprintSha1 != null) { |
| fingerprintSha1.setStringValue(""); |
| } |
| |
| // empty values are considered ok. |
| if (fileName.length() > 0) { |
| File file = new File(fileName); |
| if (file.isFile()) { |
| // attempt to load the debug key. |
| try { |
| DebugKeyProvider provider = new DebugKeyProvider(fileName, |
| null /* storeType */, null /* key gen output */); |
| PrivateKey key = provider.getDebugKey(); |
| X509Certificate certificate = (X509Certificate)provider.getCertificate(); |
| |
| if (key == null || certificate == null) { |
| showErrorMessage("Unable to find debug key in keystore!"); |
| return false; |
| } |
| |
| if (fingerprintMd5 != null) { |
| fingerprintMd5.setStringValue( |
| FingerprintUtils.getFingerprint(certificate, "MD5")); |
| } |
| |
| if (fingerprintSha1 != null) { |
| fingerprintSha1.setStringValue( |
| FingerprintUtils.getFingerprint(certificate, "SHA1")); |
| } |
| |
| Date today = new Date(); |
| if (certificate.getNotAfter().compareTo(today) < 0) { |
| showErrorMessage("Certificate is expired!"); |
| return false; |
| } |
| |
| if (certificate.getNotBefore().compareTo(today) > 0) { |
| showErrorMessage("Certificate validity is in the future!"); |
| return false; |
| } |
| |
| // we're good! |
| clearErrorMessage(); |
| return true; |
| } catch (GeneralSecurityException e) { |
| handleException(e); |
| return false; |
| } catch (IOException e) { |
| handleException(e); |
| return false; |
| } catch (KeytoolException e) { |
| handleException(e); |
| return false; |
| } catch (AndroidLocationException e) { |
| handleException(e); |
| return false; |
| } |
| |
| |
| } else { |
| // file does not exist. |
| showErrorMessage("Not a valid keystore path."); |
| return false; // Apply/OK must be disabled |
| } |
| } |
| |
| clearErrorMessage(); |
| return true; |
| } |
| |
| @Override |
| public Text getTextControl(Composite parent) { |
| setValidateStrategy(VALIDATE_ON_KEY_STROKE); |
| return super.getTextControl(parent); |
| } |
| |
| /** |
| * Set the error message from a {@link Throwable}. If the exception has no message, try |
| * to get the message from the cause. |
| * @param t the Throwable. |
| */ |
| private void handleException(Throwable t) { |
| String msg = t.getMessage(); |
| if (msg == null) { |
| Throwable cause = t.getCause(); |
| if (cause != null) { |
| handleException(cause); |
| } else { |
| setErrorMessage("Uknown error when getting the debug key!"); |
| } |
| |
| return; |
| } |
| |
| // valid text, display it. |
| showErrorMessage(msg); |
| } |
| } |
| } |