Hi i have the following code used to read contacts from an andorid phone and write them to a file - i am trying to show the progress of the process below is the code for reading and writing the contacts
package com.lightcone.readcontacts;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
public class ReadContacts extends Activity {
// To suppress notational clutter and make structure clearer, define some shorthand constants.
private static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
private static final Uri PURI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
private static final String ID = ContactsContract.Contacts._ID;
private static final String DNAME = ContactsContract.Contacts.DISPLAY_NAME;
private static final String HPN = ContactsContract.Contacts.HAS_PHONE_NUMBER;
private static final String CID = ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
private static final String PNUM = ContactsContract.CommonDataKinds.Phone.NUMBER;
private static final String PHONETYPE = ContactsContract.CommonDataKinds.Phone.TYPE;
private String id;
private String name;
private String ph[];
private String phType[];
private File root;
private int phcounter;
private TextView tv;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView) findViewById(R.id.TextView01);
// Allow for up to 9 email and phone entries for a contact
ph = new String[9];
phType = new String[9];
// Check that external media available and writable
checkExternalMedia();
ContentResolver ctr = getContentResolver();
Cursor cur = ctr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
int numberOfContacts = cur.getCount();
Toast.makeText(this, "Reading" +String.valueOf(numberOfContacts)+ "Contacts", Toast.LENGTH_LONG).show();
File dir = new File (root.getAbsolutePath() + "/download");
dir.mkdirs();
File file = new File(dir, "phoneData.txt");
tv.append("Wrote " +numberOfContacts+" to "+file+"\nfor following contacts:\n");
try{
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
ContentResolver cr = getContentResolver();
Cursor cu = cr.query(URI, null, null, null, null);
if (cu.getCount() > 0) {
// Loop over all contacts
while (cu.moveToNext()) {
id = cu.getString(cu.getColumnIndex(ID));
name = cu.getString(cu.getColumnIndex(DNAME));
tv.append("\n"+id+" "+name);
phcounter = 0;
if (Integer.parseInt(cu.getString(cu.getColumnIndex(HPN))) > 0) {
Cursor pCur = cr.query(PURI, null, CID + " = ?", new String[]{id}, null);
while (pCur.moveToNext()) {
ph[phcounter] = pCur.getString(pCur.getColumnIndex(PNUM));
phType[phcounter] = pCur.getString(pCur.getColumnIndex(PHONETYPE));
phcounter ++;
}
pCur.close();
}
// Write identifiers for this contact to the SD card file
pw.println("<nc>"+name);
for(int i=0; i<phcounter; i++){
pw.println("\tphone="+ ph[i]);
}
}
}
// Flush the PrintWriter to ensure everything pending is output before closing
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i("MEDIA", "*************** File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest file? ");
} catch (IOException e) {
e.printStackTrace();
}
}
private void checkExternalMedia () {
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// We can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Can't read or write
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
root = android.os.Environment.getExternalStorageDirectory();
tv.append( "External storage: Exists="+mExternalStorageAvailable+", Writable="
+mExternalStorageWriteable+" Root="+root+"\n");
}
private String getPhoneType(String index){
if(index.trim().equals( "1")){
return "home";
} else if (index.trim().equals("2")){
return "mobile";
} else if (index.trim().equals("3")){
return "work";
} else if (index.trim().equals("7")){
return "other";
} else {
return "?";
}
}
}
use AsyncTask to write the contacts to file and show the progress bar in onPreExecute() method of AsyncTask and dismiss the progress in onPostExecute() method of AsyncTask.
Related
My tablet have Internal Storage,USB Storage,External SD card Storage.I need to access External SD card.Still I cannot get the correct answer.
You can access to SD card via adb like:
adb shell cd \$EXTERNAL_STORAGE
Or you may go over getExternalStorageState() .
If you have SD card, you can use Environment.getExternalStorageDirectory() to get root path of your SD card.
in onActivityResult you will get data
Uri selectedImageUri = data.getData();
String path = App.getPath(getActivity(), selectedImageUri);
use this method toget your file path
#TargetApi(19)
#SuppressLint("NewApi")
public static String getPath(final Activity context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/"
+ split[1];
}
// TODO handle non-primary volumes
} else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection,
selectionArgs);
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
}
if (uri == null) {
// TODO perform some logging or show user feedback
return null;
} else {
// try to retrieve the image from the media store first
// this will only work for images selected from gallery
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = context.managedQuery(uri, projection, null, null, null);
if (cursor != null) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
// this is our fallback here
return uri.getPath();
}
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri
.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri
.getAuthority());
}
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri
.getAuthority());
}
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri
.getAuthority());
}
Try this way
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello" />
<EditText android:id="#+id/myInputText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" android:lines="5"
android:minLines="3" android:gravity="top|left"
android:inputType="textMultiLine">
<requestFocus />
</EditText>
<Button android:id="#+id/saveInternalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save to Internal Storage" />
<Button android:id="#+id/getInternalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Display from Internal Storage" />
<Button android:id="#+id/saveExternalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save to External Storage" />
<Button android:id="#+id/getExternalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Display from External Storage" />
<TextView android:id="#+id/responseText"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:padding="5dp"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
AndroidStorageActivity.java
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class AndroidStorageActivity extends Activity implements OnClickListener{
private String filename = "MySampleFile.txt";
private String filepath = "MyFileStorage";
File myInternalFile;
File myExternalFile;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ContextWrapper contextWrapper = new ContextWrapper(getApplicationContext());
File directory = contextWrapper.getDir(filepath, Context.MODE_PRIVATE);
myInternalFile = new File(directory , filename);
Button saveToInternalStorage =
(Button) findViewById(R.id.saveInternalStorage);
saveToInternalStorage.setOnClickListener(this);
Button readFromInternalStorage =
(Button) findViewById(R.id.getInternalStorage);
readFromInternalStorage.setOnClickListener(this);
Button saveToExternalStorage =
(Button) findViewById(R.id.saveExternalStorage);
saveToExternalStorage.setOnClickListener(this);
Button readFromExternalStorage =
(Button) findViewById(R.id.getExternalStorage);
readFromExternalStorage.setOnClickListener(this);
//check if external storage is available and not read only
if (!isExternalStorageAvailable() || isExternalStorageReadOnly()) {
saveToExternalStorage.setEnabled(false);
}
else {
myExternalFile = new File(getExternalFilesDir(filepath), filename);
}
}
public void onClick(View v) {
EditText myInputText = (EditText) findViewById(R.id.myInputText);
TextView responseText = (TextView) findViewById(R.id.responseText);
String myData = "";
switch (v.getId()) {
case R.id.saveInternalStorage:
try {
FileOutputStream fos = new FileOutputStream(myInternalFile);
fos.write(myInputText.getText().toString().getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
myInputText.setText("");
responseText
.setText("MySampleFile.txt saved to Internal Storage...");
break;
case R.id.getInternalStorage:
try {
FileInputStream fis = new FileInputStream(myInternalFile);
DataInputStream in = new DataInputStream(fis);
BufferedReader br =
new BufferedReader(new InputStreamReader(in));
String strLine;
while ((strLine = br.readLine()) != null) {
myData = myData + strLine;
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
myInputText.setText(myData);
responseText
.setText("MySampleFile.txt data retrieved from Internal Storage...");
break;
case R.id.saveExternalStorage:
try {
FileOutputStream fos = new FileOutputStream(myExternalFile);
fos.write(myInputText.getText().toString().getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
myInputText.setText("");
responseText
.setText("MySampleFile.txt saved to External Storage...");
break;
case R.id.getExternalStorage:
try {
FileInputStream fis = new FileInputStream(myExternalFile);
DataInputStream in = new DataInputStream(fis);
BufferedReader br =
new BufferedReader(new InputStreamReader(in));
String strLine;
while ((strLine = br.readLine()) != null) {
myData = myData + strLine;
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
myInputText.setText(myData);
responseText
.setText("MySampleFile.txt data retrieved from Internal Storage...");
break;
}
}
private static boolean isExternalStorageReadOnly() {
String extStorageState = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) {
return true;
}
return false;
}
private static boolean isExternalStorageAvailable() {
String extStorageState = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(extStorageState)) {
return true;
}
return false;
}
}
you will get output like this
its so easy to get the paths of internal and external sd card paths.
for that we just need to use the reflection mehtod and no need of rooting or signing of app.
Add the following permission in manifest.
uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
Code:
final Class surfaceControlClass = Class.forName("android.os.storage.StorageManager");
Method method1 = surfaceControlClass.getMethod("getVolumePaths")
method1.setAccessible(true);
final Object object = context.getSystemService(Context.STORAGE_SERVICE);
String[] volumes = method1.invoke(object)
I'm working on a custom Content Provider for my app. This is part of a course I'm taking on Android apps, so please don't expect the rationale for doing all this to be too great ;-) The whole point here is for me to learn about CP.
I've got a previous post which goes on and on with this, but I think I've managed to simplify my problem quite a bit. So, I'm working on a "gallery app". Since I don't know how and where thumbnail images are stored on the phone, I've decided to simply use MediaStore.Images.Thumbnails to access the thumbs, and show them in my GridView.
However, to fullfill the requirements of said course, I'll write a "PhotoProvider" to load single photos, full screen, in the DetailActivity. Also, for this to make some sense, and not just be a wrapper around MediaStore.Media.Images, I'm extending that data with some EXIF tags from the JPEG file.
Having found a great post here on StackOverflow, and continuing to wrangle the source code provided in class, I've come up with the following classes. Maybe you want to take a look, and help me out (point me in the right direction)?
The URIs I'll support, are context://AUTH/photo/#, to get a specific image (given an IMAGE_ID, from the thumbnail). Furthermore, to make it a bit more interesting, I also want to be able to write data to the EXIF tag UserComment: context://AUTH/photo/#/comment/* (the comment String is the last parameter there). Does that look sensible?
Some questions: (updated)
getType() is confusing. Again, this is lending from the app course. When returning an image, I guess the type should always be image/jpeg (or maybe PNG)?
Edit: Learning more stuff, I now understand that the URI returned from the insert() method is, I guess, optional, but it often useful to return a "link" (i.e. URI) to the new (inserted) data! For my case, after updating the EXIF tags, I could return either null, or an URI to the edited photo: context://AUTH/photo/7271 (where 7271 mimicks the PHOTO_ID).
Below is my (unfinished!) code. Please have a look, especially at the query() and insert() functions :-)
PhotoContract.java
package com.example.android.galleri.app.data;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
public class PhotoContract {
public static final String CONTENT_AUTHORITY = "no.myapp.android.galleri.app";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String PATH_PHOTO = "photo";
public static final String PATH_COMMENT = "comment";
public static final class PhotoEntry {
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_PHOTO).build();
public static final String COLUMN_DISPLAY_NAME = MediaStore.Images.Media.DISPLAY_NAME;
public static final String COLUMN_DATA = MediaStore.Images.Media.DATA;
public static final String COLUMN_DESC = MediaStore.Images.Media.DESCRIPTION;
public static final String COLUMN_DATE_TAKEN = MediaStore.Images.Media.DATE_TAKEN;
public static final String COLUMN_DATE_ADDED = MediaStore.Images.Media.DATE_ADDED;
public static final String COLUMN_TITLE = MediaStore.Images.Media.TITLE;
public static final String COLUMN_SIZE = MediaStore.Images.Media.SIZE;
public static final String COLUMN_ORIENTATION = MediaStore.Images.Media.ORIENTATION;
public static final String COLUMN_EXIF_COMMENT = "UserComment";
public static final String COLUMN_EXIF_AUTHOR = "Author";
// should these simply be image/png??
public static final String CONTENT_TYPE =
ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_PHOTO;
public static final String CONTENT_ITEM_TYPE =
ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_PHOTO;
// makes an URI to a specific image_id
public static final Uri buildPhotoWithId(Long photo_id) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(photo_id)).build();
}
// since we will "redirect" the URI towards MediaStore, we need to be able to extract IMAGE_ID
public static Long getImageIdFromUri(Uri uri) {
return Long.parseLong(uri.getPathSegments().get(1)); // TODO: is it position 1??
}
// get comment to set in EXIF tag
public static String getCommentFromUri(Uri uri) {
return uri.getPathSegments().get(2); // TODO: is it position 2??
}
}
}
PhotoProvider.java:
package com.example.android.galleri.app.data;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.content.CursorLoader;
import android.util.Log;
import java.io.IOException;
public class PhotoProvider extends ContentProvider {
// The URI Matcher used by this content provider.
private static final UriMatcher sUriMatcher = buildUriMatcher();
static final int PHOTO = 100;
static final int PHOTO_SET_COMMENT = 200;
static UriMatcher buildUriMatcher() {
// 1) The code passed into the constructor represents the code to return for the root
// URI. It's common to use NO_MATCH as the code for this case. Add the constructor below.
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = PhotoContract.CONTENT_AUTHORITY;
// 2) Use the addURI function to match each of the types. Use the constants from
// WeatherContract to help define the types to the UriMatcher.
// matches photo/<any number> meaning any photo ID
matcher.addURI(authority, PhotoContract.PATH_PHOTO + "/#", PHOTO);
// matches photo/<photo id>/comment/<any comment>
matcher.addURI(authority, PhotoContract.PATH_PHOTO + "/#/" + PhotoContract.PATH_COMMENT + "/*", PHOTO_SET_COMMENT);
// 3) Return the new matcher!
return matcher;
}
#Override
public String getType(Uri uri) {
// Use the Uri Matcher to determine what kind of URI this is.
final int match = sUriMatcher.match(uri);
switch (match) {
case PHOTO_SET_COMMENT:
return PhotoContract.PhotoEntry.CONTENT_TYPE;
case PHOTO:
return PhotoContract.PhotoEntry.CONTENT_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
#Override
public boolean onCreate() {
return true; // enough?
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
MatrixCursor retCursor = new MatrixCursor(projection);
// open the specified image through the MediaStore to get base columns
// then open image file through ExifInterface to get detail columns
Long IMAGE_ID = PhotoContract.PhotoEntry.getImageIdFromUri(uri);
//Uri baseUri = Uri.parse("content://media/external/images/media");
Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
baseUri = Uri.withAppendedPath(baseUri, ""+ IMAGE_ID);
String[] MS_projection = {
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DESCRIPTION,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media.TITLE,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.ORIENTATION};
// http://androidsnippets.com/get-file-path-of-gallery-image
Cursor c = getContext().getContentResolver().query(baseUri, MS_projection, null, null, null);
// dump fields (the ones we want -- assuming for now we want ALL fields, in SAME ORDER) into MatrixCursor
Object[] row = new Object[projection.length];
row[0] = c.getString(0); // DISPLAY_NAME
row[1] = c.getBlob(1); // etc
row[2] = c.getString(2);
row[3] = c.getLong(3);
row[4] = c.getLong(4);
row[5] = c.getString(5);
row[6] = c.getInt(6);
row[7] = c.getInt(7);
// NB! Extra +2 fields, for EXIF data.
try {
ExifInterface exif = new ExifInterface((String)row[1]);
row[8] = exif.getAttribute("UserComment");
row[9] = exif.getAttribute("Author");
} catch (IOException e) {
e.printStackTrace();
}
retCursor.addRow(row);
return retCursor;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
String comment_to_set = PhotoContract.PhotoEntry.getCommentFromUri(uri);
Long IMAGE_ID = PhotoContract.PhotoEntry.getImageIdFromUri(uri);
Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
baseUri = Uri.withAppendedPath(baseUri, ""+ IMAGE_ID);
// get DATA (path/filename) from MediaStore -- only need that specific piece of information
String[] MS_projection = {MediaStore.Images.Media.DATA};
// http://androidsnippets.com/get-file-path-of-gallery-image
Cursor c = getContext().getContentResolver().query(baseUri, MS_projection, null, null, null);
String thumbData = c.getString(0);
try {
ExifInterface exif = new ExifInterface(thumbData);
exif.setAttribute("UserComment", comment_to_set);
exif.saveAttributes();
} catch (IOException e) {
e.printStackTrace();
}
return PhotoContract.PhotoEntry.buildPhotoWithId(IMAGE_ID); // return URI to this specific image
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
I believe I managed to figure this out, and it wasn't so mysterious after all. Writing a custom content, in this case, provider really boiled down to implementing my own query and update methods. I didn't need any insert or delete, since I'm only reading the MediaStore.
To get started, I designed two URIs; one for query and one for update. The query URI followed the pattern, content://AUTH/photo/#, where # is the IMAGE_ID from MediaStore.Images.Thumbnails. This method essentially runs another query against MediaStore.Images.Media, to get the "standard" data on the specific image, but then gets some additional meta data on the image via the ExifInterface. All this is returned in a MatrixCursor.
The update URI matches the pattern, content://AUTH/photo/#/comment, which then sets (writes) the UserComment EXIF tag in the file with that ID -- again via the ExifInterface.
To answer my question, I'm posting updated code from my PhotoContract and PhotoProvider classes. Essentially, if you're writing a content provider which does not interface the SQLite database -- but something else on the device -- all you need to do is implement those operations in the appropriate methods in your provider class.
PhotoContract
import android.content.ContentResolver;
import android.content.ContentUris;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
public class PhotoContract {
public static final String CONTENT_AUTHORITY = "com.example.android.myFunkyApp.app";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String PATH_PHOTO = "photo";
public static final String PATH_COMMENT = "comment";
//public static final String PATH_AUTHOR = "author";
public static final class ThumbEntry {
public static final String COLUMN_THUMB_ID = MediaStore.Images.Thumbnails._ID;
public static final String COLUMN_DATA = MediaStore.Images.Thumbnails.DATA;
public static final String COLUMN_IMAGE_ID = MediaStore.Images.Thumbnails.IMAGE_ID;
}
public static final class PhotoEntry {
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_PHOTO).build();
public static final String COLUMN_IMAGE_ID = MediaStore.Images.Media._ID;
public static final String COLUMN_DISPLAY_NAME = MediaStore.Images.Media.DISPLAY_NAME;
public static final String COLUMN_DATA = MediaStore.Images.Media.DATA;
public static final String COLUMN_DESC = MediaStore.Images.Media.DESCRIPTION;
public static final String COLUMN_DATE_TAKEN = MediaStore.Images.Media.DATE_TAKEN;
public static final String COLUMN_DATE_ADDED = MediaStore.Images.Media.DATE_ADDED;
public static final String COLUMN_TITLE = MediaStore.Images.Media.TITLE;
public static final String COLUMN_SIZE = MediaStore.Images.Media.SIZE;
public static final String COLUMN_ORIENTATION = MediaStore.Images.Media.ORIENTATION;
public static final String COLUMN_EXIF_COMMENT = "UserComment";
//public static final String COLUMN_EXIF_AUTHOR = "Author";
public static final String CONTENT_TYPE =
ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_PHOTO;
public static final String CONTENT_ITEM_TYPE =
ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_PHOTO;
// makes an URI to a specific image_id
public static final Uri buildPhotoUriWithId(Long photo_id) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(photo_id)).build();
}
// since we will "redirect" the URI towards MediaStore, we need to be able to extract IMAGE_ID
public static Long getImageIdFromUri(Uri uri) {
return Long.parseLong(uri.getPathSegments().get(1)); // always in position 1
}
}
}
PhotoProvider
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.AbstractWindowedCursor;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.CursorWrapper;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.content.CursorLoader;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
public class PhotoProvider extends ContentProvider {
// The URI Matcher used by this content provider.
private static final UriMatcher sUriMatcher = buildUriMatcher();
static final int PHOTO = 100;
static final int PHOTO_COMMENT = 101;
static final int PHOTO_AUTHOR = 102;
static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = PhotoContract.CONTENT_AUTHORITY;
// matches photo/<any number> meaning any photo ID
matcher.addURI(authority, PhotoContract.PATH_PHOTO + "/#", PHOTO);
// matches photo/<photo id>/comment/ (comment text in ContentValues)
matcher.addURI(authority, PhotoContract.PATH_PHOTO + "/#/" + PhotoContract.PATH_COMMENT, PHOTO_COMMENT);
// matches photo/<photo id>/author/ (author name in ContentValues)
//matcher.addURI(authority, PhotoContract.PATH_PHOTO + "/#/" + PhotoContract.PATH_AUTHOR, PHOTO_AUTHOR);
return matcher;
}
#Override
public String getType(Uri uri) {
// Use the Uri Matcher to determine what kind of URI this is.
final int match = sUriMatcher.match(uri);
// Note: We always return single row of data, so content-type is always "a dir"
switch (match) {
case PHOTO_COMMENT:
return PhotoContract.PhotoEntry.CONTENT_TYPE;
case PHOTO_AUTHOR:
return PhotoContract.PhotoEntry.CONTENT_TYPE;
case PHOTO:
return PhotoContract.PhotoEntry.CONTENT_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
#Override
public boolean onCreate() {
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
MatrixCursor retCursor = new MatrixCursor(projection);
// open the specified image through the MediaStore to get base columns
// then open image file through ExifInterface to get detail columns
Long IMAGE_ID = PhotoContract.PhotoEntry.getImageIdFromUri(uri);
Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
baseUri = Uri.withAppendedPath(baseUri, ""+ IMAGE_ID);
// http://androidsnippets.com/get-file-path-of-gallery-image
// run query against MediaStore, projection = null means "get all fields"
Cursor c = getContext().getContentResolver().query(baseUri, null, null, null, null);
if (!c.moveToFirst()) {
return null;
}
// match returned fields against projection, and copy into row[]
Object[] row = new Object[projection.length];
int i = 0;
/* // Cursor.getType() Requires API level > 10...
for (String colName : projection) {
int idx = c.getColumnIndex(colName);
if (idx <= 0) return null; // ERROR
int colType = c.getType(idx);
switch (colType) {
case Cursor.FIELD_TYPE_INTEGER: {
row[i++] = c.getLong(idx);
break;
}
case Cursor.FIELD_TYPE_FLOAT: {
row[i++] = c.getFloat(idx);
break;
}
case Cursor.FIELD_TYPE_STRING: {
row[i++] = c.getString(idx);
break;
}
case Cursor.FIELD_TYPE_BLOB: {
row[i++] = c.getBlob(idx);
break;
}
}
}
*/
//http://stackoverflow.com/questions/11658239/cursor-gettype-for-api-level-11
CursorWrapper cw = (CursorWrapper)c;
Class<?> cursorWrapper = CursorWrapper.class;
Field mCursor = null;
try {
mCursor = cursorWrapper.getDeclaredField("mCursor");
mCursor.setAccessible(true);
AbstractWindowedCursor abstractWindowedCursor = (AbstractWindowedCursor)mCursor.get(cw);
CursorWindow cursorWindow = abstractWindowedCursor.getWindow();
int pos = abstractWindowedCursor.getPosition();
// NB! Expect resulting cursor to contain data in same order as projection!
for (String colName : projection) {
int idx = c.getColumnIndex(colName);
// simple solution: If column name NOT FOUND in MediaStore, assume it's an EXIF tag
// and skip
if (idx >= 0) {
if (cursorWindow.isNull(pos, idx)) {
//Cursor.FIELD_TYPE_NULL
row[i++] = null;
} else if (cursorWindow.isLong(pos, idx)) {
//Cursor.FIELD_TYPE_INTEGER
row[i++] = c.getLong(idx);
} else if (cursorWindow.isFloat(pos, idx)) {
//Cursor.FIELD_TYPE_FLOAT
row[i++] = c.getFloat(idx);
} else if (cursorWindow.isString(pos, idx)) {
//Cursor.FIELD_TYPE_STRING
row[i++] = c.getString(idx);
} else if (cursorWindow.isBlob(pos, idx)) {
//Cursor.FIELD_TYPE_BLOB
row[i++] = c.getBlob(idx);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
// have now handled the first i fields in projection. If there are any more, we expect
// these to be valid EXIF tags. Should obviously make this more robust...
try {
ExifInterface exif = new ExifInterface((String) row[2]);
while (i < projection.length) {
row[i] = exif.getAttribute("UserComment"); //projection[i]); // String (or null)
i++;
}
} catch (IOException e) {
e.printStackTrace();
}
retCursor.addRow(row);
return retCursor;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// URI identifies IMAGE_ID and which EXIF tag to set; content://AUTH/photo/<image_id>/comment or /author
// first, get IMAGE_ID and prepare URI (to get file path from MediaStore)
Long IMAGE_ID = PhotoContract.PhotoEntry.getImageIdFromUri(uri);
Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
baseUri = Uri.withAppendedPath(baseUri, ""+ IMAGE_ID);
// get DATA (path/filename) from MediaStore -- only need that specific piece of information
String[] MS_projection = {MediaStore.Images.Media.DATA};
// http://androidsnippets.com/get-file-path-of-gallery-image
Cursor c = getContext().getContentResolver().query(baseUri, MS_projection, null, null, null);
if (!c.moveToFirst()) return -1; // can't get image path/filename...
String thumbData = c.getString(0);
// then, use URIMatcher to identify the "operation" (i.e., which tag to set)
final int match = sUriMatcher.match(uri);
String EXIF_tag;
switch (match) {
case PHOTO_COMMENT: {
EXIF_tag = "UserComment";
break;
}
case PHOTO_AUTHOR: {
EXIF_tag = "Author";
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (EXIF_tag == null) return -1;
// finally, set tag in image
try {
ExifInterface exif = new ExifInterface(thumbData);
String textToSet = values.get(EXIF_tag).toString();
if (textToSet == null)
throw new IOException("Can't retrieve text to set from ContentValues, on key = " + EXIF_tag);
if (textToSet.length() > 48)
throw new IOException("Data too long (" + textToSet.length() + "), on key = " + EXIF_tag);
exif.setAttribute(EXIF_tag, textToSet);
exif.saveAttributes();
} catch (IOException e) {
e.printStackTrace();
}
return 1; // 1 image updated
}
}
Hi guys i have this code which i use to get contacts from the phone and store them in vcf format,the code works properly for contacts that have only one number but i keep getting duplicates for contacts with multiple numbers i.e contacts that have home number,work number e.t.c...any help will be appreciated.....this is my code below
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.ContactsContract;
import android.util.Log;
public class Show_contacts extends Activity {
Cursor cursor;
ArrayList<String> vCard;
String vfile;
static Context mContext;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = Show_contacts.this;
getVCF();
}
public static void getVCF() {
final String vfile = "Contacts.vcf";
Cursor phones = mContext.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
phones.moveToFirst();
for (int i = 0; i < phones.getCount(); i++) {
String lookupKey = phones.getString(phones.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
AssetFileDescriptor fd;
try {
fd = mContext.getContentResolver().openAssetFileDescriptor(uri, "r");
FileInputStream fis = fd.createInputStream();
byte[] buf = new byte[(int) fd.getDeclaredLength()];
fis.read(buf);
String VCard = new String(buf);
String path = Environment.getExternalStorageDirectory().toString() + File.separator + vfile;
FileOutputStream mFileOutputStream = new FileOutputStream(path, true);
mFileOutputStream.write(VCard.toString().getBytes());
phones.moveToNext();
Log.d("Vcard", VCard);
mFileOutputStream.close();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
We have to check lookupKey. It works for me.
Cursor cursor;
ArrayList<String> vCard;
Context mContext;
ArrayList<String> saveLkKey;
String vcfFile;
String currentTime;
String rootStorage;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.save_contact_fragment, container,
false);
mContext = getActivity();
rootStorage = Environment.getExternalStorageDirectory() + "/";
Time today = new Time(Time.getCurrentTimezone());
today.setToNow();
currentTime = today.monthDay + "" + (today.month + 1) + "" + today.year
+ "-" + today.format("%k%M%S") + "";
vcfFile = "myContacts" + currentTime + ".vcf";
Button btnSaveVcf = (Button) view.findViewById(R.id.btnSave1);
btnSaveVcf.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getVCF(vcfFile);
}
});
return view;
}
public void getVCF(String vfile) {
Cursor phones = mContext.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,
null, null);
phones.moveToFirst();
for (int i = 0; i < phones.getCount(); i++) {
String lookupKey = phones.getString(phones
.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
lookupKey = lookupKey.trim();
if (saveLkKey == null) {
saveLkKey = new ArrayList<String>();
saveLkKey.add(lookupKey);
} else {
if (isDuplicate(lookupKey)) {
phones.moveToNext();
continue;
} else {
saveLkKey.add(lookupKey);
}
}
Uri uri = Uri.withAppendedPath(
ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
AssetFileDescriptor fd;
try {
fd = mContext.getContentResolver().openAssetFileDescriptor(uri,
"r");
FileInputStream fis = fd.createInputStream();
byte[] buf = new byte[(int) fd.getDeclaredLength()];
fis.read(buf);
String VCard = new String(buf);
String path = rootStorage + vfile;
FileOutputStream mFileOutputStream = new FileOutputStream(path,
true);
mFileOutputStream.write(VCard.toString().getBytes());
phones.moveToNext();
Log.d("Vcard", VCard);
mFileOutputStream.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
public boolean isDuplicate(String lkKey) {
for (String s : saveLkKey) {
if (s.equals(lkKey)) {
return true;
}
}
return false;
}
I am currently working on my first android app for my comp prog class. The app records contacts onto a txt file and stores it on the SD card. I am able to retrieve the file manually, but I now want the app to send the txt file back to me automatically after it has been created. Please, what is the easiest way to do this. I use eclipse. It doesn't matter how the txt file gets to me, it just needs to...that is the point of the lesson. This works fine and all permissions and stuff are in order. I need to know what to put to send and where. Any additional permissions? I appreciate any help. Thanks in advance. Kevin
package com.lightcone.readcontacts;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.TextView;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
public class ReadContacts extends Activity {
private static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
private static final Uri PURI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
private static final Uri EURI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
private static final Uri AURI = ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI;
private static final String ID = ContactsContract.Contacts._ID;
private static final String DNAME = ContactsContract.Contacts.DISPLAY_NAME;
private static final String HPN = ContactsContract.Contacts.HAS_PHONE_NUMBER;
private static final String LOOKY = ContactsContract.Contacts.LOOKUP_KEY;
private static final String CID = ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
private static final String EID = ContactsContract.CommonDataKinds.Email.CONTACT_ID;
private static final String AID = ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID;
private static final String PNUM = ContactsContract.CommonDataKinds.Phone.NUMBER;
private static final String PHONETYPE = ContactsContract.CommonDataKinds.Phone.TYPE;
private static final String EMAIL = ContactsContract.CommonDataKinds.Email.DATA;
private static final String EMAILTYPE = ContactsContract.CommonDataKinds.Email.TYPE;
private static final String STREET = ContactsContract.CommonDataKinds.StructuredPostal.STREET;
private static final String CITY = ContactsContract.CommonDataKinds.StructuredPostal.CITY;
private static final String STATE = ContactsContract.CommonDataKinds.StructuredPostal.REGION;
private static final String POSTCODE = ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE;
private static final String COUNTRY = ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY;
private String id;
private String lookupKey;
private String name;
private String street;
private String city;
private String state;
private String postcode;
private String country;
private String ph[];
private String phType[];
private String em[];
private String emType[];
private File root;
private int emcounter;
private int phcounter;
private int addcounter;
private TextView tv;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView) findViewById(R.id.TextView01);
em = new String[5];
emType = new String[5];
ph = new String[5];
phType = new String[5];
checkExternalMedia();
File dir = new File (root.getAbsolutePath() + "/download");
dir.mkdirs();
File file = new File(dir, "phoneData.txt");
tv.append("Wrote " +file+"\nfor following contacts:\n");
try{
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
ContentResolver cr = getContentResolver();
Cursor cu = cr.query(URI, null, null, null, null);
if (cu.getCount() > 0) {
while (cu.moveToNext()) {
street = "";
city = "";
state = "";
postcode = "";
country = "";
id = cu.getString(cu.getColumnIndex(ID));
name = cu.getString(cu.getColumnIndex(DNAME));
lookupKey = cu.getString(cu.getColumnIndex(LOOKY));
tv.append("\n"+id+" "+name);
phcounter = 0;
if (Integer.parseInt(cu.getString(cu.getColumnIndex(HPN))) > 0) {
Cursor pCur = cr.query(PURI, null, CID + " = ?", new String[]{id}, null);
while (pCur.moveToNext()) {
ph[phcounter] = pCur.getString(pCur.getColumnIndex(PNUM));
phType[phcounter] = pCur.getString(pCur.getColumnIndex(PHONETYPE));
phcounter ++;
}
pCur.close();
}
emcounter = 0;
Cursor emailCur = cr.query(EURI, null, EID + " = ?", new String[]{id}, null);
while (emailCur.moveToNext()) {
em[emcounter] = emailCur.getString(emailCur.getColumnIndex(EMAIL));
emType[emcounter] = emailCur.getString(emailCur.getColumnIndex(EMAILTYPE));
emcounter ++;
}
emailCur.close();
addcounter = 0;
Cursor addCur = cr.query(AURI, null, AID + " = ?", new String[]{id}, null);
while (addCur.moveToNext()) {
street = addCur.getString(addCur.getColumnIndex(STREET));
city = addCur.getString(addCur.getColumnIndex(CITY));
state = addCur.getString(addCur.getColumnIndex(STATE));
postcode = addCur.getString(addCur.getColumnIndex(POSTCODE));
country = addCur.getString(addCur.getColumnIndex(COUNTRY));
addcounter ++;
}
addCur.close();
pw.println(name+" ID="+id+" LOOKUP_KEY="+lookupKey);
for(int i=0; i<phcounter; i++){
pw.println(" phone="+ ph[i]+" type="+phType[i] + " ("
+ getPhoneType(phType[i]) + ") ");
}
for(int i=0; i<emcounter; i++){
pw.println(" email="+ em[i]+" type="+emType[i] + " ("
+ getEmailType(emType[i]) + ") ");
}
if(addcounter > 0){
if(street != null) pw.println(" street="+street);
if(city != null) pw.println(" city="+city);
if(state != null) pw.println(" state/region="+state);
if(postcode != null) pw.println(" postcode="+postcode);
if(country != null) pw.println(" country="+country);
}
}
}
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i("MEDIA", "*************** File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest file? ");
} catch (IOException e) {
e.printStackTrace();
}
}
private void checkExternalMedia () {
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
root = android.os.Environment.getExternalStorageDirectory();
tv.append( "External storage: Exists="+mExternalStorageAvailable+", Writable="
+mExternalStorageWriteable+" Root="+root+"\n");
}
private String getPhoneType(String index){
if(index.trim().equals( "1")){
return "home";
} else if (index.trim().equals("2")){
return "mobile";
} else if (index.trim().equals("3")){
return "work";
} else if (index.trim().equals("7")){
return "other";
} else {
return "?";
}
}
private String getEmailType(String index){
if(index.trim().equals( "1")){
return "home";
} else if (index.trim().equals("2")){
return "work";
} else if (index.trim().equals("3")){
return "other";
} else if (index.trim().equals("4")){
return "mobile";
} else {
return "?";
}
}
}
I would like to retrieve the browser history from the com.android.browser and save it into a file in sdcard. I am able to do soo for the contacts but not the browser history.Don't know what went wrong. They only prompt the error bind or column index out of range: handle 0x27a6d0.
What would be the best format for the file to be xml, txt or??
These are my codes
package fypj.ReadContacts;
import android.app.Activity;
import android.os.Bundle;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Browser;
import android.provider.Browser.BookmarkColumns;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.TextView;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
public class ReadContacts extends Activity {
// To suppress notational clutter and make structure clearer, define some shorthand constants.
private static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
private static final Uri PURI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
private static final Uri EURI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
private static final Uri AURI = ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI;
private static final String ID = ContactsContract.Contacts._ID;
private static final String DNAME = ContactsContract.Contacts.DISPLAY_NAME;
private static final String HPN = ContactsContract.Contacts.HAS_PHONE_NUMBER;
private static final String LOOKY = ContactsContract.Contacts.LOOKUP_KEY;
private static final String CID = ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
private static final String EID = ContactsContract.CommonDataKinds.Email.CONTACT_ID;
private static final String AID = ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID;
private static final String PNUM = ContactsContract.CommonDataKinds.Phone.NUMBER;
private static final String PHONETYPE = ContactsContract.CommonDataKinds.Phone.TYPE;
private static final String EMAIL = ContactsContract.CommonDataKinds.Email.DATA;
private static final String EMAILTYPE = ContactsContract.CommonDataKinds.Email.TYPE;
private static final String STREET = ContactsContract.CommonDataKinds.StructuredPostal.STREET;
private static final String CITY = ContactsContract.CommonDataKinds.StructuredPostal.CITY;
private static final String STATE = ContactsContract.CommonDataKinds.StructuredPostal.REGION;
private static final String POSTCODE = ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE;
private static final String COUNTRY = ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY;
private static final Uri URI1 = android.provider.Browser.BOOKMARKS_URI;
private static final String bHistoryTITLE = Browser.BookmarkColumns.TITLE;
private static final String bHistoryURL = Browser.BookmarkColumns.URL;
private static final String bHistoryBOOKMARK = Browser.BookmarkColumns.BOOKMARK;
private static final String bHistoryVISITS = Browser.BookmarkColumns.VISITS;
private static final String ID1 = Browser.BookmarkColumns._ID ;
//private static final String BID = Browser.BookmarkColumns.
//private static final String DNAME = Browser.BookmarkColumns.DISPLAY_NAME;
//private static final String HPN = ContactsContract.Contacts.HAS_PHONE_NUMBER;
//private static final String LOOKY = Browser.BookmarkColumns.LOOKUP_KEY;
private String id;
private String lookupKey;
private String name;
private String street;
private String city;
private String state;
private String postcode;
private String country;
private String ph[];
private String phType[];
private String em[];
private String emType[];
private String id1;
private String URL;
private String BTitle;
private String BURL;
private String BBOOKMARK;
private String BVISITS;
private String browserTITLE[];
private String browserURL[];
private String browserBOOKMARK[];
private String browserVISITS[];
private File root;
private int emcounter;
private int phcounter;
private int addcounter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Check that external media available and writable
checkExternalMedia();
Contacts();
browser();
}
/** Method to check whether external media available and writable and to find the
root of the external file system. */
private void checkExternalMedia () {
// Check external media availability. This is adapted from
// http://developer.android.com/guide/topics/data/data-storage.html#filesExternal
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// We can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Can't read or write
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
// Find the root of the external storage and output external storage info to screen
root = android.os.Environment.getExternalStorageDirectory();
}
public void Contacts(){
// Allow for up to 5 email and phone entries for a contact
em = new String[5];
emType = new String[5];
ph = new String[5];
phType = new String[5];
/** Open a PrintWriter wrapping a FileOutputStream so that we can send output from a
query of the Contacts database to a file on the SD card. Must wrap the whole thing
in a try-catch to catch file not found and i/o exceptions. Note that since we are writing
to external media we must add a WRITE_EXTERNAL_STORAGE permission to the
manifest file. Otherwise a FileNotFoundException will be thrown. */
// This will set up output to /sdcard/download/phoneData.txt if /sdcard is the root of
// the external storage. See the project WriteSDCard for more information about
// writing to a file on the SD card.
File dir = new File (root.getAbsolutePath() + "/download");
dir.mkdirs();
//File file = new File(dir, "phoneData.rtf");
File file = new File(dir, "phoneData.accdb");
try{
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
// Main loop to query the contacts database, extracting the information. See
// http://www.higherpass.com/Android/Tutorials/Working-With-Android-Contacts/
ContentResolver cr = getContentResolver();
Cursor cu = cr.query(URI, null, null, null, null);
if (cu.getCount() > 0) {
// Loop over all contacts
while (cu.moveToNext()) {
// Initialize storage variables for the new contact
street = "";
city = "";
state = "";
postcode = "";
country = "";
// Get ID information (id, name and lookup key) for this contact. id is an identifier
// number, name is the name associated with this row in the database, and
// lookupKey is an opaque value that contains hints on how to find the contact
// if its row id changed as a result of a sync or aggregation.
id = cu.getString(cu.getColumnIndex(ID));
name = cu.getString(cu.getColumnIndex(DNAME));
lookupKey = cu.getString(cu.getColumnIndex(LOOKY));
// Append list of contacts to the scrollable TextView on the screen.
// Query phone numbers for this contact (may be more than one), so use a
// while-loop to move the cursor to the next row until moveToNext() returns
// false, indicating no more rows. Store the results in arrays since there may
// be more than one phone number stored per contact. The if-statement
// enclosing everything ensures that the contact has at least one phone
// number stored in the Contacts database.
phcounter = 0;
if (Integer.parseInt(cu.getString(cu.getColumnIndex(HPN))) > 0) {
Cursor pCur = cr.query(PURI, null, CID + " = ?", new String[]{id}, null);
while (pCur.moveToNext()) {
ph[phcounter] = pCur.getString(pCur.getColumnIndex(PNUM));
phType[phcounter] = pCur.getString(pCur.getColumnIndex(PHONETYPE));
phcounter ++;
}
pCur.close();
}
// Query email addresses for this contact (may be more than one), so use a
// while-loop to move the cursor to the next row until moveToNext() returns
// false, indicating no more rows. Store the results in arrays since there may
// be more than one email address stored per contact.
emcounter = 0;
Cursor emailCur = cr.query(EURI, null, EID + " = ?", new String[]{id}, null);
while (emailCur.moveToNext()) {
em[emcounter] = emailCur.getString(emailCur.getColumnIndex(EMAIL));
emType[emcounter] = emailCur.getString(emailCur.getColumnIndex(EMAILTYPE));
emcounter ++;
}
emailCur.close();
// Query Address (assume only one address stored for simplicity). If there is
// more than one address we loop through all with the while-loop but keep
// only the last one.
addcounter = 0;
Cursor addCur = cr.query(AURI, null, AID + " = ?", new String[]{id}, null);
while (addCur.moveToNext()) {
street = addCur.getString(addCur.getColumnIndex(STREET));
city = addCur.getString(addCur.getColumnIndex(CITY));
state = addCur.getString(addCur.getColumnIndex(STATE));
postcode = addCur.getString(addCur.getColumnIndex(POSTCODE));
country = addCur.getString(addCur.getColumnIndex(COUNTRY));
addcounter ++;
}
addCur.close();
// Write identifiers for this contact to the SD card file
//pw.println(name+" ID="+id+" LOOKUP_KEY="+lookupKey);
pw.println(name);
// Write list of phone numbers for this contact to SD card file
for(int i=0; i<phcounter; i++){
//pw.println(" phone="+ ph[i]+" type="+phType[i] + " ("
//+ getPhoneType(phType[i]) + ") ");
pw.println(" phone="+ ph[i]+" ("
+ getPhoneType(phType[i]) + ") ");
}
// Write list of email addresses for this contact to SD card file
for(int i=0; i<emcounter; i++){
pw.println(" email="+ em[i]+" type="+emType[i] + " ("
+ getEmailType(emType[i]) + ") ");
}
// If street address is stored for contact, write it to SD card file
if(addcounter > 0){
if(street != null) pw.println(" street="+street);
if(city != null) pw.println(" city="+city);
if(state != null) pw.println(" state/region="+state);
if(postcode != null) pw.println(" postcode="+postcode);
if(country != null) pw.println(" country="+country);
}
}
}
// Flush the PrintWriter to ensure everything pending is output before closing
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i("MEDIA", "*************** File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest file? ");
} catch (IOException e) {
e.printStackTrace();
}
}
/** Method to return label corresponding to phone type code. Data for correspondence from
http://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Phone.html */
private String getPhoneType(String index){
if(index.trim().equals( "1")){
return "home";
} else if (index.trim().equals("2")){
return "mobile";
} else if (index.trim().equals("3")){
return "work";
} else if (index.trim().equals("7")){
return "other";
} else {
return "?";
}
}
/** Method to return label corresponding to email type code. Data for correspondence from
http://developer.android.com/reference/android/provider/ContactsContract.
CommonDataKinds.Email.html */
private String getEmailType(String index){
if(index.trim().equals( "1")){
return "home";
} else if (index.trim().equals("2")){
return "work";
} else if (index.trim().equals("3")){
return "other";
} else if (index.trim().equals("4")){
return "mobile";
} else {
return "?";
}
}
public void browser(){
//create file
File dir = new File (root.getAbsolutePath() + "/DATASTOREAGE");
dir.mkdirs();
File file = new File(dir, "browserHistory.rtf");
try{
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
// Main loop to query the contacts database, extracting the information. See
// http://www.higherpass.com/Android/Tutorials/Working-With-Android-Contacts/
ContentResolver cr = getContentResolver();
Cursor cu = cr.query(URI1, null, null, null, null);
if (cu.getCount() > 0) {
// Loop over all contacts
while (cu.moveToNext()) {
// Initialize storage variables for the new contact
BTitle = "";
BURL = "";
BBOOKMARK = "";
BVISITS = "";
// Get ID information (id, name and lookup key) for this contact. id is an identifier
// number, name is the name associated with this row in the database, and
// lookupKey is an opaque value that contains hints on how to find the contact
// if its row id changed as a result of a sync or aggregation.
id1 = cu.getString(cu.getColumnIndex(ID1));
URL = cu.getString(cu.getColumnIndex(BookmarkColumns.TITLE));
//lookupKey = cu.getString(cu.getColumnIndex(LOOKY));
// Append list of contacts to the scrollable TextView on the screen.
// Query phone numbers for this contact (may be more than one), so use a
// while-loop to move the cursor to the next row until moveToNext() returns
// false, indicating no more rows. Store the results in arrays since there may
// be more than one phone number stored per contact. The if-statement
// enclosing everything ensures that the contact has at least one phone
// number stored in the Contacts database.
phcounter = 0;
Cursor bCur = cr.query(URI1, null, ID1, new String[]{id1}, null);
while (bCur.moveToNext()) {
browserTITLE[phcounter] = bCur.getString(bCur.getColumnIndex(bHistoryTITLE));
browserURL[phcounter] = bCur.getString(bCur.getColumnIndex(bHistoryURL));
browserBOOKMARK[phcounter] = bCur.getString(bCur.getColumnIndex(bHistoryBOOKMARK));
browserVISITS[phcounter] = bCur.getString(bCur.getColumnIndex(bHistoryVISITS));
phcounter ++;
}
bCur.close();
}
// Write identifiers for this contact to the SD card file
//pw.println(name+" ID="+id+" LOOKUP_KEY="+lookupKey);
pw.println(BTitle+" ID="+id1+" LOOKUP_KEY="+lookupKey);
pw.println(BURL);
pw.println(BBOOKMARK);
pw.println(BVISITS);
}
// Flush the PrintWriter to ensure everything pending is output before closing
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i("MEDIA", "*************** File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest file? ");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Your selection column is wrong in the second query of URI1 the line should read:
Cursor bCur = cr.query(URI1, null, ID1+"=?", new String[]{id1}, null);
The second query isn't needed though. You are first quering all columns, extracting the ID from all columns. And then doing a nest query on the all columns for a particular id.
You don't need the nest query, the first query will have all the data you need.
The question of the best file format. That would depend on what is going to consume the data you are creating.