主程序:com.android.gallery3d.app.Gallery,其中在activity-alias中定义了其targetActivity为GalleryActivity,所以入口为GalleryActivity.java
显示界面:AlbumSetPage(相册缩略图)、AlbumPage(单个相册的照片缩略图)、PhotoPage(单个相片缩略图)
界面父类:ActivityState.java
界面切换:StateManager.java
数据管理:DataManager.java
布局
main.xml
<include layout="@layout/gl_root_group">
<!--头部和底部的Fragment设为隐藏-->
<Fragment
android:id="@+id/header"/>
<Fragment
android:id="@+id/footer"/>
gl_root_group.xml
<com.android.gallery3d.ui.GLRootView/>
<!--View,隐藏-->
<View
android:id="@+id/gl_root_cover"/>
//如果是从桌面小部件或者Launcher启动的时候,设置可见
Intent intent = getIntent();
if (intent != null
&& (intent.getBooleanExtra(EXTRA_FROM_WIDGET, false) || (intent
.getAction() != null && intent.getAction().equals(intent.ACTION_MAIN)))) {
View view = findViewById(R.id.gl_root_cover);
if (view != null) {
view.setVisibility(View.VISIBLE);
Log.d(TAG, "<onCreate> from widget or launcher, set gl_root_cover VISIBLE");
}
}
通过PermissionHelper(位置:vendor/proprietary/packages/apps/Gallery2/src/com/mediatek/gallery3d/util)的checkAndRequestForGallery方法获取读写的权限,再进行判断后调用initializeByIntent()
boolean granted = PermissionHelper.checkAndRequestForGallery(this);
if (granted) {
if (savedInstanceState != null) {
getStateManager().restoreFromState(savedInstanceState);
} else {
initializeByIntent();
}
} else {
mSaveInstanceState = savedInstanceState;
}
//通过intent的行为来进行判断是以何种方式打开的,从而调用对应的方法来初始化
.....
if(Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)){//获取所有本地图片
startGetContent(intent);
}else if(Intent.ACTION_PICK.equalsIgnoreCase(action)){//获取相册中的图片}
else{
startDefaultPage();//默认的方式启动
}
默认方式,一般处理
PicasaSource.showSignInReminder(this);//静态内部方法,do nothing
Bundle data = new Bundle();
//KEY_MEDIA_PATH = "media-path"
//INCLUDE_ALL = 1|2(图片|视频)
//通过getTopSetPath获取的路径赋值给AlbumSetPage的KEY_MEDIA_PATH
data.putString(AlbumSetPage.KEY_MEDIA_PATH,getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
data数据的来源追踪
//根据传入的值进行判断,返回不同的路径
switch(typeBits){
....
case INCLUDE_ALL:return TOP_SET_PATH;//TOP_SET_PATH = "/combo/{/local/all,/picasa/all}"
}
getStateManager().startState(AlbumSetPage.class, data);//页面切换,进入AlbumSetPage
public void startState(Class<? extends ActivityState> klass,Bundle data) {
Log.v(TAG, "startState " + klass);
ActivityState state = null;
try {
state = klass.newInstance();//创建传入过来的AlbumSetPage的新实例,并赋值给state
} catch (Exception e) {
throw new AssertionError(e);
}
//stack栈来管理页面逻辑切换
if (!mStack.isEmpty()) {//初次启动,mStack为空,初始化
ActivityState top = getTopState();//获取栈顶的ActivityState=null
top.transitionOnNextPause(top.getClass(), klass,
StateTransitionAnimation.Transition.Incoming);//mNextTransition=hint=StateTransitionAnimation.Transition.Incoming
if (mIsResumed) top.onPause();//false不执行
}
//什么都没处理,跳过
UsageStatistics.onContentViewChanged(
UsageStatistics.COMPONENT_GALLERY,
klass.getSimpleName());
//初始化state
state.initialize(mActivity, data);
mStack.push(new StateEntry(data, state));//入栈,保存的是AlbumSetPage
state.onCreate(data, null);//执行AlbumSetPage.onCreate()
if (mIsResumed) state.resume();//false,不执行
}
mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);//null
if (mVersionCheckDialog != null) {//不执行
mVersionCheckDialog.setOnCancelListener(this);
}
图库首页页面
initializeViews();
initializeData(data);//由GalleryActivity传入的数据,携带路径字符串
//mActivity=AlbumSetPage
mSelectionManager = new SelectionManager(mActivity, true);
mSelectionManager.setSelectionListener(this);//设置监听,mListener!=null
//初始化SlotView并设置参数,由于FancyHelper.isFancyLayoutSupported()返回的是false,因此SlotView采用的是默认的布局DefaultLayout
mSlotView = new SlotView(mActivity, mConfig.slotViewSpec,
FancyHelper.isFancyLayoutSupported());
//渲染器,控制mSlotView的显示
mAlbumSetView = new AlbumSetSlotRenderer(
mActivity, mSelectionManager, mSlotView, mConfig.labelSpec,
mConfig.placeholderColor);//实例化
mSlotView.setSlotRenderer(mAlbumSetView);//设置SlotView和渲染器的连接
mSlotView.setListener(new SlotView.SimpleListener(){//设置监听
//根据行为进行相应的操作,onDOWN/onUP/onSingleTapUp/onLongTap
@Override
public void onDown(int index){
AlbumSetPage.this.onDown(index);
}
......
}));
//AlbumSetSlotRenderer mAlbumSetView
//调用AlbumSetSlotRenderer中的setPressedIndex方法
private void onDown(int index) {
mAlbumSetView.setPressedIndex(index);
}
AlbumSetSlotRenderer.java
public void setPressedIndex(int index) {
if (mPressedIndex == index) return;
mPressedIndex = index;
mSlotView.invalidate();//重绘view/初始化
}
public void onUp(boolean followedByLongPress) {
AlbumSetPage.this.onUp(followedByLongPress);
}
private void onUp(boolean followedByLongPress) {
if (followedByLongPress) {
// Avoid showing press-up animations for long-press.
mAlbumSetView.setPressedIndex(-1);
} else {
mAlbumSetView.setPressedUp();
}
}
public void onSingleTapUp(int slotIndex) {
AlbumSetPage.this.onSingleTapUp(slotIndex);
}
public void onSingleTapUp(int slotIndex) {
if (!mIsActive) return;
if (mSelectionManager.inSelectionMode()) {
MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
if (targetSet == null) return; // Content is dirty, we shall reload soon
if (mRestoreSelectionDone) {
/// M: [BUG.ADD] fix menu display abnormal @{
if (mActionModeHandler != null) {
mActionModeHandler.closeMenu();
}
/// @}
mSelectionManager.toggle(targetSet.getPath());
mSlotView.invalidate();
} else {
if (mWaitToast == null) {
mWaitToast = Toast.makeText(mActivity, R.string.wait, Toast.LENGTH_SHORT);
}
mWaitToast.show();
}
} else {
/// M: [BUG.ADD] check slotIndex valid or not @{
if (!mAlbumSetDataAdapter.isActive(slotIndex)) {
Log.d(TAG, "<onSingleTapUp> slotIndex " + slotIndex
+ " is not active, return!");
return;
}
/// @}
// Show pressed-up animation for the single-tap.
mAlbumSetView.setPressedIndex(slotIndex);
mAlbumSetView.setPressedUp();
/// M: [PERF.MODIFY] send Message without 180ms delay for improving performance. @{
//mHandler.sendMessageDelayed(mHandler.obtainMessage(
// MSG_PICK_ALBUM, slotIndex, 0), FadeTexture.DURATION);
Log.d(TAG, "onSingleTapUp() at " + System.currentTimeMillis());
mHandler.sendMessage(mHandler.obtainMessage(MSG_PICK_ALBUM, slotIndex, 0));
mAlbumSetView.setPressedIndex(-1);
/// @}
}
}
//选取后根据不同的行为进行处理的handler
mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
mActionModeHandler.setActionModeListener(new ActionModeListener() {
@Override
public boolean onActionItemClicked(MenuItem item) {
return onItemSelected(item);
}
/// M: [BEHAVIOR.ADD] @{
public boolean onPopUpItemClicked(int itemId) {
// return if restoreSelection has done
return mRestoreSelectionDone;
}
/// @}
});
//mRootPane是一个GLView实例,把创建好的SlotView添加进去
mRootPane.addComponent(mSlotView);
@Override
protected boolean onItemSelected(MenuItem item){
Activity activity = mActivity;
switch(item.getItemId()){
case R.id.action_cancel://取消
//setResult()将数据传回启动页,要在finish()之前执行
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
return true;
case R.id.action_select:
mSelectionManager.setAutoLeaveSelectionMode(false);
mSelectionManager.enterSelectionMode();
}
}
public void setAutoLeaveSelectionMode(boolean enable) {//当选中的项目减少到0的时候,是否会自动离开选择模式,设置为false,不会自动离开
mAutoLeave = enable;
}
//进入选择模式
public void enterSelectionMode() {
if (mInSelectionMode) return;//只有声明,没有初始化,不执行
Log.d(TAG, "<enterSelectionMode>");
mInSelectionMode = true;
if (mListener != null) mListener.onSelectionModeChange(ENTER_SELECTION_MODE);//initializeViews时mSelectionManager进行过监听绑定,不为空,执行
}
定义在SelectionManager中的接口中的方法
//SelectionManager.java
public interface SelectionListener {
public void onSelectionModeChange(int mode);
public void onSelectionChange(Path path, boolean selected);
/// M: [BEHAVIOR.ADD] @{
public void onSelectionRestoreDone();
/// @}
}
AlbumSetPage.java实现
@Override
public void onSelectionModeChange(int mode) {
switch (mode) {
case SelectionManager.ENTER_SELECTION_MODE: {//进入选择模式
mActionBar.disableClusterMenu(true);
mActionModeHandler.startActionMode();
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
}
case SelectionManager.LEAVE_SELECTION_MODE: {
mActionModeHandler.finishActionMode();
if (mShowClusterMenu) {
mActionBar.enableClusterMenu(mSelectedAction, this);
}
mRootPane.invalidate();
break;
}
/// M: [BEHAVIOR.ADD] @{
// when click deselect all in menu, not leave selection mode
case SelectionManager.DESELECT_ALL_MODE:
/// @}
case SelectionManager.SELECT_ALL_MODE: {
mActionModeHandler.updateSupportedOperation();
mRootPane.invalidate();
break;
}
}
}
public void disableClusterMenu(boolean hideMenu) {
if (mActionBar != null) {
mClusterRunner = null;
if (hideMenu) {
//设置ationbar的导航模式:STANDARD(标准)、TAB(标签切换模式)、LIST(下拉列表模式)
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
}
}
ActionModeHandler.java
进入ActionMode模式,临时占用ActionBar,并为它设置layout布局
public void startActionMode() {
Activity a = mActivity;
mActionMode = a.startActionMode(this);
View customView = LayoutInflater.from(a).inflate(
R.layout.action_mode, null);//加载action_mode视图,一个imageview和button组成,相册左上角导航栏的图标
mActionMode.setCustomView(customView);//为ActionMode加载布局
mSelectionMenu = new SelectionMenu(a,
(Button) customView.findViewById(R.id.selection_menu), this);
updateSelectionMenu();
}
SelectionMenu()
SelectionMenu.java
选择菜单的初始化
public SelectionMenu(Context context, Button button, OnPopupItemClickListener listener) {
mContext = context;
mButton = button;
mPopupList = new PopupList(context, mButton);
mPopupList.addItem(R.id.action_select_all,
context.getString(R.string.select_all));//全选item
mPopupList.setOnPopupItemClickListener(listener);
mButton.setOnClickListener(this);
}
updateSelectionMenu()
ActionModeHandler.java
public void updateSelectionMenu() {
/// @}
// update title
int count = mSelectionManager.getSelectedCount();
Log.d(TAG, "<updateSelectionMenu> select count is " + count);
/// M: [BUG.MODIFY] @{
/*
String format = mActivity.getResources().getQuantityString(
R.plurals.number_of_items_selected, count);
setTitle(String.format(format, count));
*/
/// @}
// M: if current state is AlbumSetPage, title maybe albums/groups,
// so getSelectedString from AlbumSetPage
String title = null;
ActivityState topState = null;
// add empty state check to avoid JE
if (mActivity.getStateManager().getStateCount() != 0) {//mStack.size()!=0,进入AlbumSetPage时入栈了
topState = mActivity.getStateManager().getTopState();//获取顶部的ActivityState
}
if (topState != null && topState instanceof AlbumSetPage) {//对topState进行判断,是否为AlbumSetPage
title = ((AlbumSetPage) topState).getSelectedString();
} else {
String format = mActivity.getResources().getQuantityString(
R.plurals.number_of_items_selected, count);
title = String.format(format, count);
}
setTitle(title);//设置标题
// For clients who call SelectionManager.selectAll() directly, we need to ensure the
// menu status is consistent with selection manager.
mSelectionMenu.updateSelectAllMode(mSelectionManager.inSelectAllMode());
}
getSelectedCount()
SelectionManager.java
public int getSelectedCount() {
int count = mClickedSet.size();
if (mInverseSelection) {
count = getTotalCount() - count;
}
return count;
}
//DATA_CACHE_SIZE=256
private void initializeData(Bundle data) {
String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);//获取媒体路径赋值给mediaPath
mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);//通过DataManager的getMediaSet方法将字符串转为path后返回MediaObject
mSelectionManager.setSourceMediaSet(mMediaSet);
mAlbumSetDataAdapter = new AlbumSetDataLoader(
mActivity, mMediaSet, DATA_CACHE_SIZE);//初始化一些数据,并添加handler对加载、结束以及运行做处理
mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
mAlbumSetView.setModel(mAlbumSetDataAdapter);//适配器与SlotView渲染器建立联系
/// M: [FEATURE.ADD] fancy layout @{
if (FancyHelper.isFancyLayoutSupported()) {//return ENABLE_FANCY=FeatureConfig.SUPPORT_FANCY_HOMEPAGE=false;不执行
mAlbumSetDataAdapter.setFancyDataChangeListener((DataChangeListener) mSlotView);
// initialize fancy thumbnail size with DisplayMetrics
FancyHelper.initializeFancyThumbnailSizes(getDisplayMetrics());
}
/// @}
}
AlbumSetDataLoader.java
public AlbumSetDataLoader(AbstractGalleryActivity activity, MediaSet albumSet, int cacheSize) {
mSource = Utils.checkNotNull(albumSet);
mCoverItem = new MediaItem[cacheSize];
mData = new MediaSet[cacheSize];
mTotalCount = new int[cacheSize];
mItemVersion = new long[cacheSize];
mSetVersion = new long[cacheSize];
Arrays.fill(mItemVersion, MediaObject.INVALID_DATA_VERSION);//INVALID_VERSION=-1;
Arrays.fill(mSetVersion, MediaObject.INVALID_DATA_VERSION);
//处理消息----->发送者?
mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_RUN_OBJECT://3
((Runnable) message.obj).run();
return;
case MSG_LOAD_START://1
if (mLoadingListener != null) mLoadingListener.onLoadingStarted();
return;
case MSG_LOAD_FINISH://2
if (mLoadingListener != null) mLoadingListener.onLoadingFinished(false);
return;
}
}
};
}
AlbumSetPage.java内部类
private class MyLoadingListener implements LoadingListener {
@Override
public void onLoadingStarted() {
/// M: [PERF.ADD] for performance auto test@{
mLoadingFinished = false;
/// @}
setLoadingBit(BIT_LOADING_RELOAD);//mLoadingBits = 1
}
@Override
public void onLoadingFinished(boolean loadingFailed) {
/// M: [PERF.ADD] for performance auto test@{
mLoadingFinished = true;
/// @}
clearLoadingBit(BIT_LOADING_RELOAD);
/// M: [BEHAVIOR.ADD] @{
// We have to notify SelectionManager about data change,
// and this is the most proper place we could find till now
boolean inSelectionMode = (mSelectionManager != null && mSelectionManager
.inSelectionMode());
int setCount = mMediaSet != null ? mMediaSet.getSubMediaSetCount() : 0;
Log.d(TAG, "<onLoadingFinished> set count=" + setCount);
Log.d(TAG, "<onLoadingFinished> inSelectionMode=" + inSelectionMode);
mSelectionManager.onSourceContentChanged();
boolean restore = false;
if (setCount > 0 && inSelectionMode) {
if (mNeedUpdateSelection) {
mNeedUpdateSelection = false;
restore = true;
mSelectionManager.restoreSelection();
}
mActionModeHandler.updateSupportedOperation();
mActionModeHandler.updateSelectionMenu();
}
if (!restore) {
mRestoreSelectionDone = true;
}
/// @}
/// M: [BUG.ADD] @{
// ClusterAlbumSet name is designed to be localized, and the
// localized name is calculated in reload(). Therefore we may obtain
// a miss-localized name which is corresponding to the obsolete
// (previous) Locale from ClusterAlbumSet if it is not finished
// reloading. Here we re-get its name after it finished reloading,
// and set the title of action bar to be the obtained name.
if (mTitle != null && mActionBar != null && !inSelectionMode
&& (mMediaSet instanceof ClusterAlbumSet)) {
String title = mMediaSet.getName();
String subtitle =
GalleryActionBar.getClusterByTypeString(mActivity, mClusterType);
if (!mTitle.equalsIgnoreCase(title)
|| (subtitle != null && !subtitle.equalsIgnoreCase(mSubtitle))) {
mTitle = title;
mSubtitle = subtitle;
mActionBar.setTitle(mTitle);
mActionBar.setSubtitle(mSubtitle);
Log.d(TAG, "<onLoadingFinished> mTitle:" + mTitle + "mSubtitle = "
+ mSubtitle);
mActionBar.notifyDataSetChanged();
}
}
/// @}
}
}
mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_PICK_ALBUM: {
pickAlbum(message.arg1);//接收onSingleTapup()发送的消息,跳转至pickAlbum()进行处理
break;
}
default: throw new AssertionError(message.what);
}
}
};
private void pickAlbum(int slotIndex) {
if (!mIsActive) return;
/// M: [BUG.ADD] check if slotIndex is active before getMediaSet @{
if (!mAlbumSetDataAdapter.isActive(slotIndex)) {
return;
}
/// @}
MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);//相册集标识
if (targetSet == null) return; // Content is dirty, we shall reload soon
if (targetSet.getTotalMediaItemCount() == 0) {//提示为空
showEmptyAlbumToast(Toast.LENGTH_SHORT);
return;
}
hideEmptyAlbumToast();
String mediaPath = targetSet.getPath().toString();//得到相册的路径
Bundle data = new Bundle(getData());
int[] center = new int[2];
getSlotCenter(slotIndex, center);
data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
if (mGetAlbum && targetSet.isLeafAlbum()) {
Activity activity = mActivity;
Intent result = new Intent()
.putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString());
activity.setResult(Activity.RESULT_OK, result);
activity.finish();
} else if (targetSet.getSubMediaSetCount() > 0) {
data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
mActivity.getStateManager().startStateForResult(
AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
} else {
if (!mGetContent && albumShouldOpenInFilmstrip(targetSet)) {
data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
mSlotView.getSlotRect(slotIndex, mRootPane));
data.putInt(PhotoPage.KEY_INDEX_HINT, 0);
data.putString(PhotoPage.KEY_MEDIA_SET_PATH,
mediaPath);
data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP, true);
data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, targetSet.isCameraRoll());
/// M: [BUG.ADD] @{
data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, null);
/// @}
mActivity.getStateManager().startStateForResult(
FilmstripPage.class, AlbumPage.REQUEST_PHOTO, data);
/// M: [BUG.ADD] avoid show selected icon when back from album page @{
mAlbumSetView.setPressedIndex(-1);
/// @}
return;
}
data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
// We only show cluster menu in the first AlbumPage in stack
boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum);
mActivity.getStateManager().startStateForResult(
AlbumPage.class, REQUEST_DO_ANIMATION, data);//进入AlbumPage
}
}