在手机上为照片编写自定义内容提供商(第2部分)

时间:2015-04-17 13:31:22

标签: android android-contentprovider

我正在为我的应用开发自定义内容提供商。这是我使用Android应用程序的课程的一部分,所以请不要期望做到这一切的理由太过分了;-)这里的重点是让我了解CP。< / p>

我有一个previous post which goes on and on with this,但我想我已经设法简化了我的问题。所以,我正在使用#34; gallery应用程序&#34;。由于我不知道手机上存储 thumbnail 图像的方式和位置,我决定只使用MediaStore.Images.Thumbnails来访问拇指,并在我的手机中显示它们网格视图。

然而,为了满足所述课程的要求,我将写一个&#34; PhotoProvider&#34;在DetailActivity中加载单张照片,全屏。此外,为了使这一点有意义,而不仅仅是MediaStore.Media.Images的包装器,我使用JPEG文件中的一些EXIF标签扩展该数据。

找到了great post here on StackOverflow,并继续纠缠课堂上提供的源代码,我提出了以下课程。也许你想看看,帮助我(指出我正确的方向)?

我支持的URI context://AUTH/photo/#,用于从缩略图中获取特定图片(给定IMAGE_ID)。此外,为了使它更有趣,我还希望能够数据写入EXIF标记UserCommentcontext://AUTH/photo/#/comment/*(注释String是那里的最后一个参数)。那看起来合情合理吗?

有些问题:(已更新)

  1. getType()令人困惑。同样,这是从应用课程借出的。返回图片时,我想类型应始终为image/jpeg(或者PNG)?
  2.   

    编辑:了解更多内容,我现在明白从insert()方法返回的URI是可选的,但是返回&#34;链接通常很有用#34; (即URI)到新的(插入的)数据!对于我的情况,在更新EXIF标记后,我可以向已修改的照片返回null或URI:context://AUTH/photo/7271(其中7271模仿PHOTO_ID)。

    以下是我的(未完成的!)代码。请查看,尤其是query()insert()函数: - )

    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;
        }
    }
    

1 个答案:

答案 0 :(得分:0)

我相信我设法解决了这个问题,毕竟它并不是那么神秘。编写自定义内容,在这种情况下,提供程序真正归结为实现我自己的queryupdate方法。我不需要任何insertdelete,因为我只是正在阅读 MediaStore。

首先,我设计了两个URI;一个用于查询,一个用于更新。查询URI遵循模式content://AUTH/photo/#,其中#是MediaStore.Images.Thumbnails中的IMAGE_ID。该方法基本上针对MediaStore.Images.Media运行另一个查询,以获得&#34;标准&#34;关于特定图像的数据,然后通过ExifInterface在图像上获得一些额外的元数据。所有这些都以MatrixCursor返回。

更新URI与模式content://AUTH/photo/#/comment匹配,然后设置(写入)具有该ID的文件中的UserComment EXIF标记 - 再次通过{{ 1}}。

要回答我的问题,我将从我的PhotoContract和PhotoProvider课程发布更新的代码。基本上,如果您正在编写一个内容提供程序,它接口SQLite数据库 - 而是设备上的其他内容 - 您需要做的就是在适当的方法中实现这些操作你的提供者类。

PhotoContract

ExifInterface

PhotoProvider

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
        }

    }
}