图库-由新增Selfies自拍文件夾探索数据图片加载

 

这篇文章是基于图库-数据部分概述上讨论的

确定AlbumSeTabPage对应的AlbumSet实例

AlbumSetTabPage的数据加载要从其initializeData()开始

private void initializeData() {
        String mediaPath = "/local/show_and_pack_system/all";
        mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
        mSelectionManager.setSourceMediaSet(mMediaSet);

        mAlbumSetDataLoader = new AlbumSetDataLoader(
                mActivity, mMediaSet, DATA_CACHE_SIZE);
        mAlbumSetDataLoader.setLoadingListener(new MyLoadingListener());
        mAlbumSetRenderer.setModel(mAlbumSetDataLoader);
        //Gionee <liuyuankun> <2016-09-29> add for CR01764760 begin
        //Gionee hushengsong 2017-05-16 modify for 137083 begin
        if(Utils.gnVFflag&&StoryUtils.getAlbumSetFirstLoadFlag(mActivity.getAndroidContext())) {
            mAlbumSetDataLoader.resume();
        }
        //Gionee hushengsong 2017-05-16 modify for 137083 end
        //Gionee <liuyuankun> <2016-09-29> add for CR01764760 end
    }

 其mediaPath是"/local/show_and_pack_system/all"

看DataManager#getMediaObject()

public MediaObject getMediaObject(Path path) {
             //... ...
            MediaSource source = mSourceMap.get(path.getPrefix());
            //... ...
MediaObject object = source.createMediaObject(path); } public synchronized void initializeSourceMap() { if (!mSourceMap.isEmpty()) return; // the order matters, the UriSource must come last addSource(new LocalSource(mApplication)); addSource(new ComboSource(mApplication)); addSource(new FilterSource(mApplication)); addSource(new UriSource(mApplication)); addSource(new StorySource(mApplication)); addSource(new StoryCoverSource(mApplication)); addSource(new PTSource(mApplication)); addSource(new KidsModeSource(mApplication)); addSource(new TrashSource(mApplication)); addSource(new TidySource(mApplication)); addSource(new PrivacySource(mApplication)); addPluginSource(); if (mActiveCount > 0) { for (MediaSource source : mSourceMap.values()) { source.resume(); } } }

 mediaPath匹配到了LocalSource,看LocalSource的构造方法:

public LocalSource(GalleryApp context) {
        super("local");
        mApplication = context;
        mMatcher = new PathMatcher();
        mMatcher.add("/local/image", LOCAL_IMAGE_ALBUMSET);
        mMatcher.add("/local/video", LOCAL_VIDEO_ALBUMSET);
        mMatcher.add("/local/all", LOCAL_ALL_ALBUMSET);

        mMatcher.add("/local/image/*", LOCAL_IMAGE_ALBUM);
        mMatcher.add("/local/video/*", LOCAL_VIDEO_ALBUM);
        mMatcher.add("/local/all/*", LOCAL_ALL_ALBUM);
        mMatcher.add("/local/image/item/*", LOCAL_IMAGE_ITEM);
        mMatcher.add("/local/video/item/*", LOCAL_VIDEO_ITEM);
        mMatcher.add("/local/image/*/*", LOCAL_IMAGE_LIMITED_ALBUM);
        mMatcher.add("/local/video/*/*", LOCAL_VIDEO_LIMITED_ALBUM);
        mMatcher.add("/local/all/*/*", LOCAL_ALL_LIMITED_ALBUM);
        mMatcher.add("/local/other/all", LOCAL_OTHER_ALL_ALBUMSET);
        mMatcher.add("/local/show/all", LOCAL_SHOW_ALL_ALBUMSET);
        mMatcher.add("/local/pt/other/all", LOCAL_PT_OTHER_ALL_ALBUMSET);
        mMatcher.add("/local/show_and_pack_system/all", LOCAL_SHOW_ALL_AND_PACK_SYSTEM);
        mMatcher.add("/local/show_and_filter_system/all", LOCAL_SHOW_ALL_AND_FILTER_SYSTEM);


        mUriMatcher.addURI(MediaStore.AUTHORITY,
                "external/images/media/#", LOCAL_IMAGE_ITEM);
        mUriMatcher.addURI(MediaStore.AUTHORITY,
                "external/video/media/#", LOCAL_VIDEO_ITEM);
        mUriMatcher.addURI(MediaStore.AUTHORITY,
                "external/images/media", LOCAL_IMAGE_ALBUM);
        mUriMatcher.addURI(MediaStore.AUTHORITY,
                "external/video/media", LOCAL_VIDEO_ALBUM);
        mUriMatcher.addURI(MediaStore.AUTHORITY,
                "external/file", LOCAL_ALL_ALBUM);
    }

 而LocalSource#createMediaObject(path);返回的是什么子类型呢,看下面代码

@Override
    public MediaObject createMediaObject(Path path) {
        GalleryApp app = mApplication;
        switch (mMatcher.match(path)) {
            case LOCAL_ALL_ALBUMSET:
            case LOCAL_IMAGE_ALBUMSET:
            case LOCAL_VIDEO_ALBUMSET:
                return new LocalAlbumSet(path, mApplication);
            case LOCAL_IMAGE_ALBUM:
                return new LocalAlbum(path, app, mMatcher.getIntVar(0), true);
            case LOCAL_VIDEO_ALBUM:
                return new LocalAlbum(path, app, mMatcher.getIntVar(0), false);
            case LOCAL_ALL_ALBUM: {
                int bucketId = mMatcher.getIntVar(0);
                DataManager dataManager = app.getDataManager();
                MediaSet imageSet = (MediaSet) dataManager.getMediaObject(
                        LocalAlbumSet.PATH_IMAGE.getChild(bucketId));
                MediaSet videoSet = (MediaSet) dataManager.getMediaObject(
                        LocalAlbumSet.PATH_VIDEO.getChild(bucketId));
                Comparator<MediaItem> comp = DataManager.sDateTakenComparator;
                return new LocalMergeAlbum(
                        path, comp, new MediaSet[]{imageSet, videoSet}, bucketId);
            }
            case LOCAL_IMAGE_LIMITED_ALBUM:
                return new LocalAlbum(path, app, mMatcher.getIntVar(0), true, mMatcher.getLongVar(1));
            case LOCAL_VIDEO_LIMITED_ALBUM:
                return new LocalAlbum(path, app, mMatcher.getIntVar(0), false, mMatcher.getLongVar(1));
            case LOCAL_ALL_LIMITED_ALBUM: {
                int bucketId = mMatcher.getIntVar(0);
                long timeSince = mMatcher.getLongVar(1);
                DataManager dataManager = app.getDataManager();
                MediaSet imageSet = (MediaSet) dataManager.getMediaObject(Path.fromString("/local/image/"
                        + bucketId + "/" + timeSince));
                MediaSet videoSet = (MediaSet) dataManager.getMediaObject(Path.fromString("/local/video/"
                        + bucketId + "/" + timeSince));
                Comparator<MediaItem> comp = DataManager.sDateTakenComparator;
                return new LocalMergeAlbum(path, comp, new MediaSet[]{imageSet, videoSet}, bucketId);
            }
            case LOCAL_IMAGE_ITEM:
                return new LocalImage(path, mApplication, mMatcher.getIntVar(0));
            case LOCAL_VIDEO_ITEM:
                return new LocalVideo(path, mApplication, mMatcher.getIntVar(0));
            case LOCAL_OTHER_ALL_ALBUMSET:
                return new LocalOtherAlbumSet(path, mApplication);
            case LOCAL_SHOW_ALL_ALBUMSET:
                return new LocalShowAlbumSet(path, mApplication);
            case LOCAL_PT_OTHER_ALL_ALBUMSET:
                return new LocalPTOtherAlbumSet(path, mApplication);
            case LOCAL_SHOW_ALL_AND_PACK_SYSTEM:
                return new LocalShowAndPackSystemAlbumSet(path, mApplication);
            case LOCAL_SHOW_ALL_AND_FILTER_SYSTEM:
                return new LocalShowAndFilterSystemAlbumSet(path, mApplication);
            default:
                throw new RuntimeException("bad path: " + path);
        }
    }

 mMatcher.add("/local/show_and_pack_system/all", LOCAL_SHOW_ALL_AND_PACK_SYSTEM);看这句就知道其mediaPath对应的是LocalShowAndPackSystemAlbumSet

看LocalShowAndPackSystemAlbumSet

public LocalShowAndPackSystemAlbumSet(Path path, GalleryApp application) {
        super(path, nextVersionNumber());
        mGalleryApp = application;
        DataManager dataManager = application.getDataManager();
        mLocalShowAllMediaSet = dataManager.getMediaSet("/local/show/all");
        mLocalShowAllMediaSet.addContentListener(this);
    }

@Override
    public MediaSet getSubMediaSet(int index) {
        MediaSet localShowAllMediaSet = mLocalShowAllMediaSet;
        int showCount = localShowAllMediaSet.getSubMediaSetCount();
       //... ...
    }

 从上面方法结合LocalSource可以看出,LocalShowAndPackSystemAlbumSet不过是引用 LocalShowAlbumSet,下面看LocalShowAlbumSet的构造方法

public LocalShowAlbumSet(Path path, GalleryApp application) {
        super(path, nextVersionNumber());
        DataManager dataManager = application.getDataManager();
        mLocalAllMediaSet = dataManager.getMediaSet("/local/all");
        mLocalAllMediaSet.addContentListener(this);
        mLocalOtherAllMediaSet = dataManager.getMediaSet("/local/other/all");
        mLocalOtherAllMediaSet.addContentListener(this);
    }

@Override
    public MediaSet getSubMediaSet(int index) {

        int localAllCount = mLocalAllMediaSet.getSubMediaSetCount();
        int localOtherAllCount = mLocalOtherAllMediaSet.getSubMediaSetCount();
//没有隐藏和放在trash中的album if (localOtherAllCount == 0 && index < localAllCount) { return mLocalAllMediaSet.getSubMediaSet(index); } int count = -1;
//有隐藏或者在trash中的album for (int i = 0; i < localAllCount; i++) { MediaSet mediaSet = mLocalAllMediaSet.getSubMediaSet(i); int bucketId = ((LocalMergeAlbum) mediaSet).getBucketId(); if (isShowBucketId(bucketId)) { count++; } if (count == index) { return mediaSet; } } return null; } @Override public int getSubMediaSetCount() { int localAllCount = mLocalAllMediaSet.getSubMediaSetCount(); int localOtherAllCount = mLocalOtherAllMediaSet.getSubMediaSetCount();
//数目等于总album数减去other,那么other应该是隐藏的album加上已删除的在trash中但还未彻底删除的album return Utils.clamp(localAllCount - localOtherAllCount, 0, localAllCount); }

 从上面代码可以看出LocalShowAlbumSet是持有了一个LocalAlbumSet和一个LocalOtherAlbumSet。

而这两个AlbumSet是什么关系呢。从上面getSubMediaSetCount()可以看出,LocalOtherAlbumSet应该是隐藏的album加上已删除的在trash中但还未彻底删除的album,

而LocalAlbumSet是所有的包括隐藏和在trash中的album。

上面确定了albumset后,那么下面就要开始初始化所有album了

初始化albums

首先看MediaSet#getSubMediaSet()。

分析LocalAlbumSet从初始化album

1.LocalAlbumSet#getSubMediaSet()

@Override
    public MediaSet getSubMediaSet(int index) {
        return mAlbums.get(index);
    }

 @Override
    // synchronized on this function for
    //   1. Prevent calling reload() concurrently.
    //   2. Prevent calling onFutureDone() and reload() concurrently
    public synchronized long reload() {
        if (mNotifier.isDirty()) {
            if (mLoadTask != null) mLoadTask.cancel();
            mIsLoading = true;
            mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this);
        }
        if (mLoadBuffer != null) {
            mAlbums = mLoadBuffer;
            mLoadBuffer = null;
            for (MediaSet album : mAlbums) {
                album.reload();
            }
            mDataVersion = nextVersionNumber();
        }
        return mDataVersion;
    }

@Override
    public synchronized void onFutureDone(Future<ArrayList<MediaSet>> future) {
        if (mLoadTask != future) return; // ignore, wait for the latest task
        mLoadBuffer = future.get();
        /// M: [BUG.MARK] @{
        /* mIsLoading = false; */
        /// @}
        if (mLoadBuffer == null) mLoadBuffer = new ArrayList<MediaSet>();
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                notifyContentChanged();
                /// M: [BUG.ADD] @{
                // To avoid timing issue of isLoading and notifyContentChanged,
                // set mIsLoading as false here.
                mIsLoading = false;
                /// @}
            }
        });
    }

mAlbums : ArrayList<MediaSet> 真正初始化入口是MediaSet#reload()

reload()中看这句:mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this);

还有:mAlbums = mLoadBuffer;

AlbumLoader异步加载完成后,会调用onFutureDone,将创建好的ArrayList<MediaSet> 传给mLoadBuffer。

下面看

private class AlbumsLoader implements ThreadPool.Job<ArrayList<MediaSet>> {

        @Override
        @SuppressWarnings("unchecked")
        public ArrayList<MediaSet> run(JobContext jc) {
            /// M: [DEBUG.ADD] @{
            TraceHelper.traceBegin(">>>>LocalAlbumSet-AlbumsLoader");
            /// @}
            // Note: it will be faster if we only select media_type and bucket_id.
            //       need to test the performance if that is worth
            BucketEntry[] entries = BucketHelper.loadBucketEntries(
                    jc, mApplication.getContentResolver(), mType);

            /// M: [DEBUG.MODIFY] @{
            /*if (jc.isCancelled()) return null;*/
            if (jc.isCancelled()) {
                TraceHelper.traceEnd();
                return null;
            }
            /// @}

            int offset = 0;
            // Move camera and download bucket to the front, while keeping the
            // order of others.
            int index = findBucket(entries, MediaSetUtils.CAMERA_BUCKET_ID);
            if (index != -1) {
                circularShiftRight(entries, offset++, index);
            }
            
          //Gionee chenjs 2017-03-01 add for Selfie album begin
            if (com.gionee.gallery3d.util.Utils.isSupportedSelfieFile()) {
            	index = findBucket(entries, MediaSetUtils.SELFIE_BUCKET_ID);
            	if (index != -1) {
            		if (com.gionee.gallery3d.util.Utils.gnGIflag) {
            			entries[index].bucketName = mApplication.getResources().getString(R.string.selfiestan);
					} else {
						entries[index].bucketName = mApplication.getResources().getString(R.string.selfies);
					}
            		circularShiftRight(entries, offset++, index);
            	}
			}
            //Gionee chenjs 2017-03-01 add for Selfie album end
            index = findBucket(entries, MediaSetUtils.DOWNLOAD_BUCKET_ID);
            if (index != -1) {
                circularShiftRight(entries, offset++, index);
            }

            ArrayList<MediaSet> albums = new ArrayList<MediaSet>();
            DataManager dataManager = mApplication.getDataManager();
            for (BucketEntry entry : entries) {
                MediaSet album = getLocalAlbum(dataManager,
                        mType, mPath, entry.bucketId, entry.bucketName);
                albums.add(album);
            }
            /// M: [DEBUG.ADD] @{
            TraceHelper.traceEnd();
            /// @}
            return albums;
        }
    }

 看这句:BucketEntry[] entries = BucketHelper.loadBucketEntries( jc, mApplication.getContentResolver(), mType);

创建Album的构造参数来自BucketEntry,其成员如下:

public static class BucketEntry {
        public String bucketName;
        public int bucketId;
        public int dateTaken;

        public BucketEntry(int id, String name) {
            bucketId = id;
            bucketName = Utils.ensureNotNull(name);
        }

        @Override
        public int hashCode() {
            return bucketId;
        }

        @Override
        public boolean equals(Object object) {
            if (!(object instanceof BucketEntry)) return false;
            BucketEntry entry = (BucketEntry) object;
            return bucketId == entry.bucketId;
        }
    }

 看获取BucketEntry[] entries的过程:

public static BucketEntry[] loadBucketEntries(
            JobContext jc, ContentResolver resolver, int type) {
        if (ApiHelper.HAS_MEDIA_PROVIDER_FILES_TABLE) {
//主要分析该方法 return loadBucketEntriesFromFilesTable(jc, resolver, type); } else { return loadBucketEntriesFromImagesAndVideoTable(jc, resolver, type); } } private static BucketEntry[] loadBucketEntriesFromFilesTable( JobContext jc, ContentResolver resolver, int type) { Uri uri = getFilesContentUri(); /// M: [FEATURE.MARK] @{ /*Cursor cursor = resolver.query(uri, PROJECTION_BUCKET, BUCKET_GROUP_BY, null, BUCKET_ORDER_BY);*/ String whereGroup = MediaFilterSetting.getExtWhereClause(null); if (null == whereGroup || whereGroup.equals("")) { whereGroup = VIDEO_IMAGE_CLAUSE + PURE_BUCKET_GROUP_BY; } else { whereGroup = "(" + VIDEO_IMAGE_CLAUSE + ") AND (" + whereGroup + ")" + PURE_BUCKET_GROUP_BY; }
//查询数据库,从上面看,projection是ImageColumns.BUCKET_ID, FileColumns.MEDIA_TYPE,
//ImageColumns.BUCKET_DISPLAY_NAME; BUCKET_ORDER_BY = "MAX(datetaken) DESC";
//VIDEO_IMAGE_CLAUSE = "media_type=1 OR media_type=3" , PURE_BUCKET_GROUP_BY = ") GROUP BY 1,(2"
//uri = Files.getContentUri("external"); 这是一个外部存储卡查询Android外部存储器的多媒体数据库的uri
Cursor cursor = resolver.query(uri, PROJECTION_BUCKET, whereGroup, null, BUCKET_ORDER_BY); /// @} if (cursor == null) { Log.w(TAG, "cannot open local database: " + uri); return new BucketEntry[0]; } ArrayList<BucketEntry> buffer = new ArrayList<BucketEntry>(); int typeBits = 0; if ((type & MediaObject.MEDIA_TYPE_IMAGE) != 0) { typeBits |= (1 << FileColumns.MEDIA_TYPE_IMAGE); } if ((type & MediaObject.MEDIA_TYPE_VIDEO) != 0) { typeBits |= (1 << FileColumns.MEDIA_TYPE_VIDEO); } try {
//根据查询到的列数据初始化entries while (cursor.moveToNext()) { if ((typeBits & (1 << cursor.getInt(INDEX_MEDIA_TYPE))) != 0) { BucketEntry entry = new BucketEntry( cursor.getInt(INDEX_BUCKET_ID), cursor.getString(INDEX_BUCKET_NAME)); if (!buffer.contains(entry)) { buffer.add(entry); } } if (jc.isCancelled()) return null; }

//因为自拍文件夹selfies是虚拟的,并没有真正的存在一个文件夹。只是根据前置摄像标志位camera_id==1在all页面中单独显示成一个集合。
//所以查询Android多媒体数据库是查不到的,需要自己建一个BucketEntry,下面加上的就是初始化selfies的过程 //Gionee chenjs 2017-03-01 add for Selfie album begin if (com.gionee.gallery3d.util.Utils.isSupportedSelfieFile()) {
//是DCIM/Camera文件夹隐藏标志,一个是内置内存卡DCIM/Camera文件夹隐藏标志,一个是可移除的内存卡DCIM/Camera文件夹隐藏标志。隐藏了的话对应的自拍图片就不会显示出来。 boolean[] hasHide = {false, false};
//查询content://com.gionee.gallery3d.data.StoryProvider/other_folders。这是图库自带的数据库,可以查询到 cursor = resolver.query(StoryContract.OtherFolder.OTHER_FOLDER_URI, new String[]{StoryContract.OtherFolder.COLUMN_BUCKET_ID}, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { if (cursor.getInt(0) == MediaSetUtils.CAMERA_BUCKET_ID) { hasHide[0] = true; //continue; } if (cursor.getInt(0) == com.gionee.gallery3d.util.MediaSetUtils.getExternalBucketID(GalleryAppImpl.getGalleryApp().getAndroidContext())) { hasHide[1] = true; } /*if (cursor.getInt(0) == MediaSetUtils.getExternalBucketID(GalleryAppImpl.getGalleryApp().getAndroidContext())) { hasHide[1] = true; }*/ } } for (BucketEntry entry : buffer) {
//查看可移除的内存卡DCIM/Camera文件夹的自拍照片数目 if (entry.bucketId == MediaSetUtils.CAMERA_BUCKET_ID || entry.bucketId == com.gionee.gallery3d.util.MediaSetUtils.getExternalBucketID(GalleryAppImpl.getGalleryApp().getAndroidContext())) { cursor = resolver.query(uri, LocalImage.PROJECTION, ImageColumns.DATA + " like? and camera_id=? and " + ImageColumns.MIME_TYPE + " =?", new String[]{"%"+Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera" + "%", String.valueOf(1), "image/jpeg"}, ImageColumns.DATE_TAKEN + " DESC, " + ImageColumns._ID + " DESC"); int count = 0; if (cursor != null) { count = cursor.getCount(); } //查看内置的内存卡DCIM/Camera文件夹的自拍照片数目 cursor = resolver.query(uri, LocalImage.PROJECTION, ImageColumns.DATA + " like? and camera_id=? and " + ImageColumns.MIME_TYPE + " =?", new String[]{"%"+ com.gionee.gallery3d.util.MediaSetUtils.getExternalPath() + "/DCIM/Camera" + "%", String.valueOf(1), "image/jpeg"}, ImageColumns.DATE_TAKEN + " DESC, " + ImageColumns._ID + " DESC"); int count1 = 0; if (cursor != null) { count1 = cursor.getCount(); } //如果存在一个有自拍照片且没有隐藏的DCIM/Camera文件夹就创建这个对应Selfies的BucketEntry if (!(hasHide[0] || count == 0) || !(hasHide[1] || count1 == 0)) { buffer.add(new BucketEntry(MediaSetUtils.SELFIE_BUCKET_ID, "selfie")); break; } } } } //Gionee chenjs 2017-03-01 add for Selfie album end } finally { Utils.closeSilently(cursor); } return buffer.toArray(new BucketEntry[buffer.size()]); }

 具体代码解析看注释。BucketEntry数组就初始化完成了。那BucketEntry的构造参数是Bucketid和name。那么Bucketid怎么和album对应的呢?

怎么根据Bucketid知道Album中有哪些MediaItem的呢?

看LocalAlbumSet.AlbumsLoader#run()中的这几句,在entries初始化完后开始创建albums(MediaSet)

for (BucketEntry entry : entries) {
                MediaSet album = getLocalAlbum(dataManager,
                        mType, mPath, entry.bucketId, entry.bucketName);
                albums.add(album);
            }

 看LocalAlbumSet#getLocalAlbum()

private MediaSet getLocalAlbum(
            DataManager manager, int type, Path parent, int id, String name) {
        synchronized (DataManager.LOCK) {
            Path path = parent.getChild(id);
            MediaObject object = manager.peekMediaObject(path);
            if (object != null) return (MediaSet) object;
            switch (type) {
                case MEDIA_TYPE_IMAGE:
                    return new LocalAlbum(path, mApplication, id, true, name);
                case MEDIA_TYPE_VIDEO:
                    return new LocalAlbum(path, mApplication, id, false, name);
                case MEDIA_TYPE_ALL:
                    Comparator<MediaItem> comp = DataManager.sDateTakenComparator;
                    return new LocalMergeAlbum(path, comp, new MediaSet[] {
                            getLocalAlbum(manager, MEDIA_TYPE_IMAGE, PATH_IMAGE, id, name),
                            getLocalAlbum(manager, MEDIA_TYPE_VIDEO, PATH_VIDEO, id, name)}, id);
            }
            throw new IllegalArgumentException(String.valueOf(type));
        }
    }

 最终初始化的album子类型又创建LocalAlbumSet时传入的Path有关,就是该与具体的bucketid无关。

跟踪代码分别有一下几种,来自MediaObject:

public static final String MEDIA_TYPE_IMAGE_STRING = "image";
    public static final String MEDIA_TYPE_VIDEO_STRING = "video";
    public static final String MEDIA_TYPE_ALL_STRING = "all";
public static int getTypeFromString(String s) {
//字段分别对应 if (MEDIA_TYPE_ALL_STRING.equals(s)) return MediaObject.MEDIA_TYPE_ALL; if (MEDIA_TYPE_IMAGE_STRING.equals(s)) return MediaObject.MEDIA_TYPE_IMAGE; if (MEDIA_TYPE_VIDEO_STRING.equals(s)) return MediaObject.MEDIA_TYPE_VIDEO; throw new IllegalArgumentException(s); }

 而从之前的分析可以知道,LocalAlbumSet对应的是/local/all;LocalOtherAlbumSet对应的是/local/other/all;

所以该流程中,LocalAlbumSet实例化的LocalMergeAlbum。可以看出,LocalMergeAlbum的构造参数包括了

new MediaSet[] { getLocalAlbum(manager, MEDIA_TYPE_IMAGE, PATH_IMAGE, id, name), getLocalAlbum(manager,MEDIA_TYPE_VIDEO, PATH_VIDEO, id, name)}

就是LocalMergeAlbum封装了两个LocalAlbum,一个是所有MediaItem都是image类型的,一个所有MediaItem都是video类型的。

LocalAlbum根据媒体类型和bucketid去初始化MediaItem

album根据bucketId初始化MediaItem

下面看LocalALbum是如何初始化MediaItem的,下面看LocalAlbum的构造方法:

public LocalAlbum(Path path, GalleryApp application, int bucketId,
            boolean isImage, String name) {
        super(path, nextVersionNumber());
        mApplication = application;
        mResolver = application.getContentResolver();
        mBucketId = bucketId;
        /// M: [BUG.MODIFY] While this is no photo in Camera folder. should set the folder name @{
        /* mName = name;*/
        if (isCameraRoll() && name.equals("")) {
            mName = application.getResources().getString(R.string.folder_camera);
            Log.d(TAG, "<LocalAlbum> mName = " + mName);
        } else {
            mName = name;
        }
        /// @}
        mIsImage = isImage;
//初始化查询参数,可以看到,都是去查询多媒体数据库,mBaseUri = Images.Media.EXTERNAL_CONTENT_URI或者mBaseUri = Video.Media.EXTERNAL_CONTENT_URI; if (isImage) { mWhereClause = ImageColumns.BUCKET_ID + " = ?"; mOrderClause = ImageColumns.DATE_TAKEN + " DESC, " + ImageColumns._ID + " DESC"; mBaseUri = Images.Media.EXTERNAL_CONTENT_URI; mProjection = LocalImage.PROJECTION; mItemPath = LocalImage.ITEM_PATH; } else { mWhereClause = VideoColumns.BUCKET_ID + " = ?"; mOrderClause = VideoColumns.DATE_TAKEN + " DESC, " + VideoColumns._ID + " DESC"; mBaseUri = Video.Media.EXTERNAL_CONTENT_URI; mProjection = LocalVideo.PROJECTION; mItemPath = LocalVideo.ITEM_PATH; } /// M: [FEATURE.ADD] @{ exInitializeWhereClause(); /// @}
//这里的uri是用于监听媒体数据库变化的。而不是用于获取数据 //Gionee chenjs 2017-03-01 add for Selfie album begin Uri[] uris; if (com.gionee.gallery3d.util.Utils.isSupportedSelfieFile()) { uris = new Uri[]{mBaseUri, StoryContract.OtherFolder.OTHER_FOLDER_URI}; } else { uris = new Uri[]{mBaseUri}; } //Gionee chenjs 2017-03-01 add for Selfie album end //Gionee <zhangjinbiao> modify for <150516> begin // mNotifier = new ChangeNotifier(this, mBaseUri, application);
//如果是Selfies文件夹,还需监听多一个uri mNotifier = new ChangeNotifier(this, uris, application); //Gionee <zhangjinbiao> modify for <150516> end }

 获取MediaItem的入口函数LocalAlbum#getMediaItem()

@Override
    public ArrayList<MediaItem> getMediaItem(int start, int count) {
        DataManager dataManager = mApplication.getDataManager();
//结合构造时初始化的uri和该方法指定的范围构造一个新的uri Uri uri = mBaseUri.buildUpon() .appendQueryParameter("limit", start + "," + count).build(); ArrayList<MediaItem> list = new ArrayList<MediaItem>(); GalleryUtils.assertNotInRenderThread(); /// M: [DEBUG.ADD] @{ TraceHelper.traceBegin(">>>>LocalAlbum-query"); /// @}
//从上面的分析可以看到,bucketid是从Android多媒体数据库中查询出来,而selfies文件夹的bucketid在多媒体数据库是没有对应的,是在
//BucketHelper#loadBucketEntriesFromFilesTable()中根据是否有自拍照片和是否隐藏来考虑是否加入这个对应selfies的bucketid的。
//所以在获取构造MediaItem需要的信息使,其他bucketid对应的集合是可以根据bucketid通过数据查到对应的信息,而selfies则需要间接的去获取
//需要通过数据库中查询有自拍照片,而且必须是DCIM/Camera路径的。 //Gionee chenjs 2017-03-01 add for Selfie album begin Cursor cursor = null; if (com.gionee.gallery3d.util.Utils.isSupportedSelfieFile()) { //mark if selfie folder has been hided; boolean[] hasHide = {false, false};
//查询图库自带的数据库,Camera文件夹的隐藏情况,如果隐藏了,则selfies就不显示了 cursor = mApplication.getContentResolver().query(StoryContract.OtherFolder.OTHER_FOLDER_URI, new String[]{StoryContract.OtherFolder.COLUMN_BUCKET_ID}, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { if (cursor.getInt(0) == MediaSetUtils.CAMERA_BUCKET_ID) { hasHide[0] = true; } if (cursor.getInt(0) == com.gionee.gallery3d.util.MediaSetUtils.getExternalBucketID(mApplication.getAndroidContext())) { hasHide[1] = true; } } } //bucketid为SELFIE_BUCKET_ID时走该if if (mBucketId == MediaSetUtils.SELFIE_BUCKET_ID && mIsImage) { //有且只有一个不隐藏的DCIM/Camera文件夹 if (!(hasHide[0] && hasHide[1]) && (hasHide[0] || hasHide[1])) { try { if (hasHide[0]) { cursor = mResolver.query(uri, mProjection, ImageColumns.DATA + " like? and " + "camera_id=?", new String[]{ "%" + Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera" + "%", String.valueOf(1)}, mOrderClause); } else if (com.gionee.gallery3d.util.MediaSetUtils.getExternalBucketID(mApplication.getAndroidContext()) != 0 && hasHide[1]) { cursor = mResolver.query(uri, mProjection, ImageColumns.DATA + " like? and " + "camera_id=?", new String[]{ "%" + com.gionee.gallery3d.util.MediaSetUtils.getExternalPath() + "/DCIM/Camera" + "%", String.valueOf(1)}, mOrderClause); } } catch (Exception e) { // TODO: handle exception } ArrayList<Integer> ids = new ArrayList<>(); if (cursor != null) {//收集隐藏了的DCIM/Camera文件夹中的自拍照片的id。下面会过滤掉这些id,剩下其他存储器中DCIM/Camera文件夹中的自拍照片 while (cursor.moveToNext()) { ids.add(cursor.getInt(0)); //id must at the first column } } StringBuilder whereClause = new StringBuilder(); int length = ids.size(); if (length > 0) {
//构造过滤隐藏的DCIM/Camera文件夹的自拍照片的查询子句 whereClause.append(ImageColumns._ID + " not in ("); for (int i = 0; i < length; i++) { whereClause.append(String.valueOf(ids.get(i))); if (i == (length -1)) { whereClause.append(")"); } else { whereClause.append(","); } } whereClause.append(" and "); } //路径中含/DCIM/Camera且camera_id=1(前置摄像头拍出来的) whereClause.append("("); whereClause.append(ImageColumns.DATA + " like "); whereClause.append("'%/DCIM/Camera%'"); whereClause.append(" and " + "camera_id=1"); whereClause.append(")"); cursor = mResolver.query( uri, mProjection, whereClause.toString(), null, mOrderClause); } else {
//不符合有且只有一个不隐藏的DCIM/Camera文件夹的,就来到这里 try { cursor = mResolver.query( uri, mProjection, ImageColumns.DATA + " like? and " + "camera_id=?", new String[] { "%" + "/DCIM/Camera" + "%", String.valueOf(1) }, mOrderClause); } catch (Exception e) { // TODO: handle exception } } } else {
//如果bucketid不是对应selfies的,就走这里。直接用传入的bucketid去查多媒体数据库 //Gionee <GN_Oversea_Bug> <rench> <20170428> <modify> for 126488 beign try { cursor = mResolver.query( uri, mProjection, mWhereClause, new String[]{String.valueOf(mBucketId)}, mOrderClause); } catch (Exception e) { // TODO: handle exception } //Gionee <GN_Oversea_Bug> <rench> <20170428> <modify> for 126488 end } } else {
//如果不支持selfies文件夹的项目,直接走这里 cursor = mResolver.query( uri, mProjection, mWhereClause, new String[]{String.valueOf(mBucketId)}, mOrderClause); } //Gionee chenjs 2017-03-01 add for Selfie album end /// M: [DEBUG.ADD] @{ TraceHelper.traceEnd(); /// @} if (cursor == null) { Log.w(TAG, "query fail: " + uri); return list; } try {
//构造MediaItem的最后一步。使用cursor去初始化MediaItem while (cursor.moveToNext()) { int id = cursor.getInt(0); // _id must be in the first column Path childPath = mItemPath.getChild(id); /// M: [DEBUG.ADD] @{ TraceHelper.traceBegin(">>>>LocalAlbum-loadOrUpdateItem"); /// @} MediaItem item = loadOrUpdateItem(childPath, cursor, dataManager, mApplication, mIsImage); /// M: [DEBUG.ADD] @{ TraceHelper.traceEnd(); /// @} list.add(item); } } finally { cursor.close(); } return list; }

 

分析LocalOtherAlbumSet从初始化album

看LocalShowAlbumSet构造方法中的这句:mLocalOtherAllMediaSet = dataManager.getMediaSet("/local/other/all");

LocalShowAlbumSet#getSubMediaSet(int index)

@Override
    public MediaSet getSubMediaSet(int index) {

        int localAllCount = mLocalAllMediaSet.getSubMediaSetCount();
        int localOtherAllCount = mLocalOtherAllMediaSet.getSubMediaSetCount();
//如果没有的话,mLocalAllMediaSet中所有album都是可以显示,进入下面的if if (localOtherAllCount == 0 && index < localAllCount) { return mLocalAllMediaSet.getSubMediaSet(index); } int count = -1;
//如果有些隐藏的则要执行下面的代码 for (int i = 0; i < localAllCount; i++) { MediaSet mediaSet = mLocalAllMediaSet.getSubMediaSet(i); int bucketId = ((LocalMergeAlbum) mediaSet).getBucketId(); if (isShowBucketId(bucketId)) { count++; } if (count == index) { return mediaSet; } } return null; }

 可以看到mLocalOtherAllMediaSet在LocalShowAlbumSet中的价值只是看下是否有隐藏的文件夹

 

加入selfies,在其他地方加入的代码解析

在StoryAlbumSet加入的代码

private ArrayList<LocalMediaItem> refreshExistingItems(JobContext jc) {
            String whereClause = getWhereClause();
            final ArrayList<LocalMediaItem> itemsToMakeStory = new ArrayList<>();
            DataManager dataManager = mApp.getDataManager();
            Cursor imageCursor = mApp.getContentResolver().query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, LocalImage.PROJECTION, whereClause, null,
                    MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC, "
                            + MediaStore.Images.ImageColumns._ID + " DESC");
           //加入selfies相册隐藏时,过滤掉。因为selfies比较特别,不是一个实际存在的文件夹。
          //Gionee chenjs 2017-03-01 add for Selfie album begin
            final ArrayList<Integer> hideId = new ArrayList<>();
            if (Utils.isSupportedSelfieFile()) {
            	boolean hasHide = false;
            	Cursor cursor = mApp.getContentResolver().query(StoryContract.OtherFolder.OTHER_FOLDER_URI,
            			new String[]{StoryContract.OtherFolder.COLUMN_BUCKET_ID}, null, null, null);
            	
            	if (cursor != null) {
            		while (cursor.moveToNext()) {
            			if (cursor.getInt(0) == MediaSetUtils.SELFIE_BUCKET_ID) {
            				hasHide = true;
            				break;
            			}
            			
            		}
            	}
            	if (hasHide) {
            		cursor = mApp.getContentResolver().query(Images.Media.EXTERNAL_CONTENT_URI, 
            				LocalImage.PROJECTION, 
            				//Gionee <GN_Oversea_Bug> <rench> <20170316> <modify> for 84148 beign
            				//ImageColumns.DATA + " like? and " + "camera_id=?", new String[]{"%"+Environment.getExternalStorageDirectory().toString()
            				ImageColumns.DATA + " like? and " + "camera_id=?", new String[]{"%"
            				//Gionee <GN_Oversea_Bug> <rench> <20170316> <modify> for 84148 end
            				+ "/DCIM/Camera" + "%", String.valueOf(1)}, null);
            	}
            	
            	if (cursor != null) {
            		while (cursor.moveToNext()) {
            			hideId.add(cursor.getInt(0));
            		}
            		cursor.close();
            	}
			}
            //Gionee chenjs 2017-03-01 add for Selfie album end
            
            int imageCount = 0;
            if (imageCursor != null) {
                try {
                    imageCount = imageCursor.getCount();
                    if (imageCursor.getCount() > 0) {
                        itemsToMakeStory.ensureCapacity(imageCount);
                        while (imageCursor.moveToNext()) {
                            if (jc.isCancelled()) {
                                return null;
                            }
                            int id = imageCursor.getInt(0);  // _id must be in the first column
                            Path childPath = mImageBasePath.getChild(id);
                            MediaItem item = LocalAlbum.loadOrUpdateItem(childPath, imageCursor,
                                    dataManager, mApp, true);
                            
                            //Gionee chenjs 2017-03-01 add for Selfie album begin
                            if (Utils.isSupportedSelfieFile()) {
                            	if (!hideId.contains(item.getId())) {
                            		itemsToMakeStory.add((LocalMediaItem) item);
                            	}
                            //Gionee chenjs 2017-03-01 add for Selfie album end
							} else {
								itemsToMakeStory.add((LocalMediaItem) item);
							}
                        }
                    }

                } finally {
                    Utils.closeSilently(imageCursor);
                }
            }

            if (jc.isCancelled()) {
                return null;
            }

            Cursor videoCursor = mApp.getContentResolver().query(
                    MediaStore.Video.Media.EXTERNAL_CONTENT_URI, LocalVideo.PROJECTION, whereClause, null,
                    MediaStore.Video.VideoColumns.DATE_TAKEN + " DESC, "
                            + MediaStore.Video.VideoColumns._ID + " DESC");
            if (videoCursor != null) {
                try {
                    int videoCount = videoCursor.getCount();
                    if (videoCount > 0) {
                        itemsToMakeStory.ensureCapacity(imageCount + videoCount);
                        while (videoCursor.moveToNext()) {
                            if (jc.isCancelled()) {
                                return null;
                            }

                            int id = videoCursor.getInt(0);  // _id must be in the first column
                            Path childPath = mVideoBasePath.getChild(id);
                            MediaItem item = LocalAlbum.loadOrUpdateItem(childPath, videoCursor,
                                    dataManager, mApp, false);
                            itemsToMakeStory.add((LocalMediaItem) item);
                        }
                    }
                } finally {
                    Utils.closeSilently(videoCursor);
                }
            }
            return itemsToMakeStory;
        }

 

还需在TrashMediaItem,TrashMediaInfo,TrashDB中加入camera_id字段相关的代码

在ThemeBitmapLoader,StoryCoverALbum中加入相关代码。

在ActionMdoeHandler#computeMenuOptions()中加入以下代码,指定selfies文件夹不允许删除,重命名,移到其他专辑操作。

//Gionee chenjs 2017-03-01 add for Selfie album begin
        if (Utils.isSupportedSelfieFile()) {
        	if (setPaths != null) {
        		for (Path path : setPaths) {
        			if (path.toString().equals("/local/all/" + MediaSetUtils.SELFIE_BUCKET_ID)) {
        				operation &= ~MediaObject.SUPPORT_DELETE;
        				operation &= ~MediaObject.SUPPORT_RENAME_FOLDER;
        				operation &= ~MediaObject.SUPPORT_MOVE_TO_OTHER;
        			}
        		}
        	}
		}
        //Gionee chenjs 2017-03-01 add for Selfie album end

 

posted on 2017-06-05 21:28  淡淡的宁静  阅读(680)  评论(0)    收藏  举报

导航