我修改了 Android SDK 示例中的示例,以便用户可以在手机中添加和保存联系人详细信息,同时引入一个名为“地理标签照片”的新照片实体。用户有两个选项(画廊或相机)来导入图像。
但是当用户从图库中选择图像时应用程序崩溃,如果用户拍照也是如此。
下面是我的课:
package com.example.android.contactmanager;
public final class ContactAdder extends Activity implements OnAccountsUpdateListener
{
public static final String TAG = "ContactsAdder";
public static final String ACCOUNT_NAME =
"com.example.android.contactmanager.ContactsAdder.ACCOUNT_NAME";
public static final String ACCOUNT_TYPE =
"com.example.android.contactmanager.ContactsAdder.ACCOUNT_TYPE";
private final int CAMERA_PICTURE = 1;
private final int GALLERY_PICTURE = 2;
private ImageView userPictureImageView;
private Intent pictureActionIntent = null;
private ArrayList<AccountData> mAccounts;
private AccountAdapter mAccountAdapter;
private Spinner mAccountSpinner;
private EditText mContactEmailEditText;
private ArrayList<Integer> mContactEmailTypes;
private Spinner mContactEmailTypeSpinner;
private EditText mContactNameEditText;
private EditText mContactPhoneEditText;
private ArrayList<Integer> mContactPhoneTypes;
private Spinner mContactPhoneTypeSpinner;
private Button mContactSaveButton;
private AccountData mSelectedAccount;
private EditText mContactGeoPhotoEditText;
/**
* Called when the activity is first created. Responsible for initializing the UI.
*/
@Override
public void onCreate(Bundle savedInstanceState)
{
Log.v(TAG, "Activity State: onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.contact_adder);
Button buttonLoadImage = (Button) findViewById(R.id.buttonLoadPicture);
buttonLoadImage.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startDialog();
}
});
// Obtain handles to UI objects
mAccountSpinner = (Spinner) findViewById(R.id.accountSpinner);
mContactNameEditText = (EditText) findViewById(R.id.contactNameEditText);
mContactPhoneEditText = (EditText) findViewById(R.id.contactPhoneEditText);
mContactEmailEditText = (EditText) findViewById(R.id.contactEmailEditText);
mContactPhoneTypeSpinner = (Spinner) findViewById(R.id.contactPhoneTypeSpinner);
mContactEmailTypeSpinner = (Spinner) findViewById(R.id.contactEmailTypeSpinner);
mContactSaveButton = (Button) findViewById(R.id.contactSaveButton);
mContactGeoPhotoEditText = (EditText) findViewById(R.id.PhotoLocation);
mContactPhoneTypes = new ArrayList<Integer>();
mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_HOME);
mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
mContactEmailTypes = new ArrayList<Integer>();
mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_HOME);
mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_WORK);
mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_MOBILE);
mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_OTHER);
// Prepare model for account spinner
mAccounts = new ArrayList<AccountData>();
mAccountAdapter = new AccountAdapter(this, mAccounts);
mAccountSpinner.setAdapter(mAccountAdapter);
// Populate list of account types for phone
ArrayAdapter<String> adapter;
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Iterator<Integer> iter;
iter = mContactPhoneTypes.iterator();
while (iter.hasNext()) {
adapter.add(ContactsContract.CommonDataKinds.Phone.getTypeLabel(
this.getResources(),
iter.next(),
getString(R.string.undefinedTypeLabel)).toString());
}
mContactPhoneTypeSpinner.setAdapter(adapter);
mContactPhoneTypeSpinner.setPrompt(getString(R.string.selectLabel));
// Populate list of account types for email
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
iter = mContactEmailTypes.iterator();
while (iter.hasNext()) {
adapter.add(ContactsContract.CommonDataKinds.Email.getTypeLabel(
this.getResources(),
iter.next(),
getString(R.string.undefinedTypeLabel)).toString());
}
mContactEmailTypeSpinner.setAdapter(adapter);
mContactEmailTypeSpinner.setPrompt(getString(R.string.selectLabel));
AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
// Register handlers for UI elements
mAccountSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long i) {
updateAccountSelection();
}
public void onNothingSelected(AdapterView<?> parent) {
// We don't need to worry about nothing being selected, since Spinners don't allow
// this.
}
});
mContactSaveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onSaveButtonClicked();
}
});
}
@Override
//working original
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
//try with integrated
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == GALLERY_PICTURE)
{
Uri selectedImage = data.getData();
if (selectedImage != null)
{
// User had pick an image.
Cursor cursor = getContentResolver().query(selectedImage, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
cursor.moveToFirst();
// Link to the image
final String imageFilePath = cursor.getString(0);
File photos = new File(imageFilePath);
Toast picturePath = Toast.makeText(this, imageFilePath, Toast.LENGTH_LONG);
picturePath.show();
Bitmap b = decodeFile(photos);
b = Bitmap.createScaledBitmap(b, 150, 150, true);
userPictureImageView.setImageBitmap(b);
cursor.close();
// ImageView imageView = (ImageView) findViewById(R.id.imgView);
// imageView.setImageBitmap(BitmapFactory.decodeFile(imageFilePath));
}
else
{
Toast toast = Toast.makeText(this, "No Image is selected.", Toast.LENGTH_LONG);
toast.show();
}
}
else if (requestCode == CAMERA_PICTURE) {
CheckEnableGPS();
if (data.getExtras() != null) {
// here is the image from camera
Bitmap bitmap = (Bitmap) data.getExtras().get("data");
userPictureImageView.setImageBitmap(bitmap);
ImageView imageView = (ImageView) findViewById(R.id.imgView);
}
}
}
private Bitmap decodeFile(File f) {
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale++;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
}
catch (FileNotFoundException e) {
}
return null;
}
private void startDialog() {
AlertDialog.Builder myAlertDialog = new AlertDialog.Builder(this);
myAlertDialog.setTitle("Select Pictures Option");
myAlertDialog.setMessage("How do you want to set your picture?");
myAlertDialog.setPositiveButton("Gallery", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
pictureActionIntent = new Intent(Intent.ACTION_GET_CONTENT, null);
pictureActionIntent.setType("image/*");
pictureActionIntent.putExtra("return-data", true);
startActivityForResult(pictureActionIntent, GALLERY_PICTURE);
}
});
myAlertDialog.setNegativeButton("Camera", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
pictureActionIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(pictureActionIntent, CAMERA_PICTURE);
}
});
myAlertDialog.show();
}
private void CheckEnableGPS()
{
String provider = Settings.Secure.getString(getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(!provider.equals("")){
//GPS Enabled
Toast.makeText(ContactAdder.this, "GPS Enabled: " + provider, Toast.LENGTH_LONG).show();
}
else
{
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage("Please make sure that you turn on the GPS setting and have a clear view of the sky.");
dlgAlert.setTitle("Kindly Remind That...");
dlgAlert.setPositiveButton("OK", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface arg0, int arg1)
{
try
{
Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
startActivity(intent);
}//end try
catch(Exception e)
{
Toast.makeText(getBaseContext(), "", Toast.LENGTH_LONG).show();
}//end catch
}//end onClick()
}).create();
dlgAlert.setCancelable(true);
dlgAlert.create().show();
}
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
private void geoTag(String absolutePath, double latitude, double longitude) {
// TODO Auto-generated method stub
}
/**
* Actions for when the Save button is clicked. Creates a contact entry and terminates the
* activity.
*/
private void onSaveButtonClicked() {
Log.v(TAG, "Save button clicked");
createContactEntry();
finish();
}
/**
* Creates a contact entry from the current UI values in the account named by mSelectedAccount.
*/
protected void createContactEntry() {
// Get values from UI
String name = mContactNameEditText.getText().toString();
String phone = mContactPhoneEditText.getText().toString();
String email = mContactEmailEditText.getText().toString();
int phoneType = mContactPhoneTypes.get(
mContactPhoneTypeSpinner.getSelectedItemPosition());
int emailType = mContactEmailTypes.get(
mContactEmailTypeSpinner.getSelectedItemPosition());
// String geotagPhoto = mContactGeoPhotoEditText.getText().toString() ;
// Prepare contact creation request
//
// Note: We use RawContacts because this data must be associated with a particular account.
// The system will aggregate this with any other data for this contact and create a
// corresponding entry in the ContactsContract.Contacts provider for us.
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName())
.build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
.build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)
.build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, email)
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)
.build());
// ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
// .withValue(ContactsContract.CommonDataKinds.Note.DATA1, geotagPhoto)
// .build());
// Ask the Contact provider to create a new contact
Log.i(TAG,"Selected account: " + mSelectedAccount.getName() + " (" + mSelectedAccount.getType() + ")");
Log.i(TAG,"Creating contact: " + name);
try {
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
// Display warning
Context ctx = getApplicationContext();
CharSequence txt = getString(R.string.contactCreationFailure);
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(ctx, txt, duration);
toast.show();
// Log exception
Log.e(TAG, "Exceptoin encoutered while inserting contact: " + e);
}
}
/**
* Called when this activity is about to be destroyed by the system.
*/
@Override
public void onDestroy() {
// Remove AccountManager callback
AccountManager.get(this).removeOnAccountsUpdatedListener(this);
super.onDestroy();
}
/**
* Updates account list spinner when the list of Accounts on the system changes. Satisfies
* OnAccountsUpdateListener implementation.
*/
public void onAccountsUpdated(Account[] a) {
Log.i(TAG, "Account list update detected");
// Clear out any old data to prevent duplicates
mAccounts.clear();
// Get account data from system
AuthenticatorDescription[] accountTypes = AccountManager.get(this).getAuthenticatorTypes();
// Populate tables
for (int i = 0; i < a.length; i++) {
// The user may have multiple accounts with the same name, so we need to construct a
// meaningful display name for each.
String systemAccountType = a[i].type;
AuthenticatorDescription ad = getAuthenticatorDescription(systemAccountType,
accountTypes);
AccountData data = new AccountData(a[i].name, ad);
mAccounts.add(data);
}
// Update the account spinner
mAccountAdapter.notifyDataSetChanged();
}
/**
* Obtain the AuthenticatorDescription for a given account type.
* @param type The account type to locate.
* @param dictionary An array of AuthenticatorDescriptions, as returned by AccountManager.
* @return The description for the specified account type.
*/
private static AuthenticatorDescription getAuthenticatorDescription(String type,
AuthenticatorDescription[] dictionary) {
for (int i = 0; i < dictionary.length; i++) {
if (dictionary[i].type.equals(type)) {
return dictionary[i];
}
}
// No match found
throw new RuntimeException("Unable to find matching authenticator");
}
/**
* Update account selection. If NO_ACCOUNT is selected, then we prohibit inserting new contacts.
*/
private void updateAccountSelection() {
// Read current account selection
mSelectedAccount = (AccountData) mAccountSpinner.getSelectedItem();
}
/**
* A container class used to represent all known information about an account.
*/
private class AccountData {
private String mName;
private String mType;
private CharSequence mTypeLabel;
private Drawable mIcon;
/**
* @param name The name of the account. This is usually the user's email address or
* username.
* @param description The description for this account. This will be dictated by the
* type of account returned, and can be obtained from the system AccountManager.
*/
public AccountData(String name, AuthenticatorDescription description) {
mName = name;
if (description != null) {
mType = description.type;
// The type string is stored in a resource, so we need to convert it into something
// human readable.
String packageName = description.packageName;
PackageManager pm = getPackageManager();
if (description.labelId != 0) {
mTypeLabel = pm.getText(packageName, description.labelId, null);
if (mTypeLabel == null) {
throw new IllegalArgumentException("LabelID provided, but label not found");
}
} else {
mTypeLabel = "";
}
if (description.iconId != 0) {
mIcon = pm.getDrawable(packageName, description.iconId, null);
if (mIcon == null) {
throw new IllegalArgumentException("IconID provided, but drawable not " +
"found");
}
} else {
mIcon = getResources().getDrawable(android.R.drawable.sym_def_app_icon);
}
}
}
public String getName() {
return mName;
}
public String getType() {
return mType;
}
public CharSequence getTypeLabel() {
return mTypeLabel;
}
public Drawable getIcon() {
return mIcon;
}
public String toString() {
return mName;
}
}
/**
* Custom adapter used to display account icons and descriptions in the account spinner.
*/
private class AccountAdapter extends ArrayAdapter<AccountData> {
public AccountAdapter(Context context, ArrayList<AccountData> accountData) {
super(context, android.R.layout.simple_spinner_item, accountData);
setDropDownViewResource(R.layout.account_entry);
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// Inflate a view template
if (convertView == null) {
LayoutInflater layoutInflater = getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.account_entry, parent, false);
}
TextView firstAccountLine = (TextView) convertView.findViewById(R.id.firstAccountLine);
TextView secondAccountLine = (TextView) convertView.findViewById(R.id.secondAccountLine);
ImageView accountIcon = (ImageView) convertView.findViewById(R.id.accountIcon);
// Populate template
AccountData data = getItem(position);
firstAccountLine.setText(data.getName());
secondAccountLine.setText(data.getTypeLabel());
Drawable icon = data.getIcon();
if (icon == null) {
icon = getResources().getDrawable(android.R.drawable.ic_menu_search);
}
accountIcon.setImageDrawable(icon);
return convertView;
}
}
}
logcat如下:</p>
12-26 15:15:13.628: V/ContactManager(377): Activity State: onCreate()
12-26 15:15:13.878: W/Resources(377): Converting to string: TypedValue{t=0x12/d=0x0 a=2 r=0x7f05000d}
12-26 15:28:33.378: V/ContactManager(410): Activity State: onCreate()
12-26 15:28:33.698: W/Resources(410): Converting to string: TypedValue{t=0x12/d=0x0 a=2 r=0x7f05000d}
12-26 15:29:17.698: I/TAG(410): You clicked item 1 at position 0
12-26 15:29:17.718: I/Show Contact Clicked:(410): Tan Chi WeeEmail: null
12-26 15:29:17.827: D/dalvikvm(410): GC_EXTERNAL_ALLOC freed 72K, 49% free 2782K/5379K, external 2032K/2137K, paused 53ms
12-26 15:29:20.018: W/KeyCharacterMap(410): No keyboard for id 0
12-26 15:29:20.018: W/KeyCharacterMap(410): Using default keymap: /system/usr/keychars/qwerty.kcm.bin
12-26 15:29:20.158: W/Resources(410): Converting to string: TypedValue{t=0x12/d=0x0 a=2 r=0x7f05000d}
12-26 15:29:21.578: D/ContactManager(410): mAddAccountButton clicked
12-26 15:29:21.648: V/ContactsAdder(410): Activity State: onCreate()
12-26 15:29:21.758: I/ContactsAdder(410): Account list update detected
12-26 15:29:26.408: W/IInputConnectionWrapper(410): showStatusIcon on inactive InputConnection
12-26 15:29:29.348: D/AndroidRuntime(410): Shutting down VM
12-26 15:29:29.348: W/dalvikvm(410): threadid=1: thread exiting with uncaught exception (group=0x40015560)
12-26 15:29:29.359: E/AndroidRuntime(410): FATAL EXCEPTION: main
12-26 15:29:29.359: E/AndroidRuntime(410): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=2, result=-1, data=Intent { dat=content://media/external/images/media/1 (has extras) }} to activity {com.example.android.contactmanager/com.example.android.contactmanager.ContactAdder}: java.lang.NullPointerException
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.ActivityThread.deliverResults(ActivityThread.java:2532)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.ActivityThread.handleSendResult(ActivityThread.java:2574)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.ActivityThread.access$2000(ActivityThread.java:117)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:961)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.os.Handler.dispatchMessage(Handler.java:99)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.os.Looper.loop(Looper.java:130)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.ActivityThread.main(ActivityThread.java:3683)
12-26 15:29:29.359: E/AndroidRuntime(410): at java.lang.reflect.Method.invokeNative(Native Method)
12-26 15:29:29.359: E/AndroidRuntime(410): at java.lang.reflect.Method.invoke(Method.java:507)
12-26 15:29:29.359: E/AndroidRuntime(410): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-26 15:29:29.359: E/AndroidRuntime(410): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-26 15:29:29.359: E/AndroidRuntime(410): at dalvik.system.NativeStart.main(Native Method)
12-26 15:29:29.359: E/AndroidRuntime(410): Caused by: java.lang.NullPointerException
12-26 15:29:29.359: E/AndroidRuntime(410): at com.example.android.contactmanager.ContactAdder.onActivityResult(ContactAdder.java:221)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.Activity.dispatchActivityResult(Activity.java:3908)
12-26 15:29:29.359: E/AndroidRuntime(410): at android.app.ActivityThread.deliverResults(ActivityThread.java:2528)
12-26 15:29:29.359: E/AndroidRuntime(410): ... 11 more
12-26 15:29:31.818: I/Process(410): Sending signal. PID: 410 SIG: 9