package com.airplayer.activity.fetchpicture; import android.app.ProgressDialog; import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; import android.support.v4.app.DialogFragment; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.widget.EditText; import android.support.v7.widget.SearchView; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.airplayer.listener.EasyRecyclerViewListener; import com.airplayer.multitask.DownloadURLTask; import com.airplayer.R; import com.airplayer.adapter.AirAdapter; import com.airplayer.fragment.dialog.ReplacePicDialogFragment; import com.airplayer.model.Picture; import com.airplayer.util.StorageUtils; import com.airplayer.util.StringUtils; import com.facebook.drawee.view.SimpleDraweeView; import java.util.ArrayList; import java.util.List; /** * Created by ZiyiTsang on 15/7/12. * Activity that start when user click the top picture of * { @link com.airplayer.fragment.singleitem.AlbumFragment } which is an album art * or { @link com.airplayer.fragment.singleitem.ArtistFragment } which is an artist picture * This activity will search a picture about the item that pass with intent before start * The item is a { @link com.airplayer.model.PictureGettable } instance, * in this app is an { @link com.airplayer.model.Album } or an { @link com.airplayer.model.Artist } */ public abstract class FetchPictureActivity extends AppCompatActivity { /** * Key of { @link #mItem } * { @link #mItem } 的键 */ public static final String EXTRA_QUERY_KEYWORD = "extra_query_keyword"; private String mQueryKeyword = null; public static final String EXTRA_SAVE_NAME = "extra_save_name"; private String mSaveName; /** * Instance of { @link com.airplayer.model.PictureGettable }, pass when start { @link com.airplayer.activity.FetchPictureActivity }. * { @link com.airplayer.model.PictureGettable } 的实例, 启动{ @link com.airplayer.activity.FetchPictureActivity } 时传入。 */ /** * a link to search a album art, use as a param to execute { @link #executeDownloadTask }. * 查询专辑封面的链接,执行 { @link #executeDownloadTask } 时作为传入参数。 */ public static final String SEARCH_LINK_ALBUM_ART = "https://api.douban.com/v2/music/search?q="; /** * a link to search a artist picture, use as a param to execute { @link com.airplayer.multitask.DownloadURLTask }. * 查询艺人图片的链接,执行 { @link com.airplayer.multitask.DownloadURLTask } 时作为传入参数。 */ public static final String SEARCH_LINK_ARTIST_PICTURE = "http://image.baidu.com/i?tn=baiduimagejson&word="; /** * Image url array list fetch from { @link com.airplayer.multitask.DownloadURLTask }. * 图片 url 的数组列表,从 { @link com.airplayer.multitask.DownloadURLTask } 获取。 */ private ArrayList<Picture> mPictureList = new ArrayList<>(); // handle download image task /** * What value of message that will be sent when { @link #downloadImage } download succeed. * 在 { @link #downloadImage } 方法下载成功时发送的 message 的 what 值。 */ private static final int MSG_DOWNLOAD_PICTURE_SUCCEED = 1; private static final int MSG_DOWNLOAD_PICTURE_FAIL = 2; /** * What value of message that will be sent when { @link com.airplayer.multitask.DownloadURLTask } download fail. * 在 { @link com.airplayer.multitask.DownloadURLTask } 下载失败时发送的 message 的 what 值。 */ private static final int MSG_DOWNLOAD_IMAGE_URL_FAIL = 3; private static final int MSG_NO_RESULT_FOUND_OR_DECODE_FAIL = 4; private ProgressDialog progress; /** * If msg is { @link #MSG_DOWNLOAD_PICTURE_SUCCEED } dismiss progress and set resultCode to RESULT_OK. * If msg is { @link #MSG_DOWNLOAD_IMAGE_URL_FAIL } make a toast to tell user. * 如果 msg 是 { @link #MSG_DOWNLOAD_PICTURE_SUCCEED } 撤销进度条并设置 resultCode 为 RESULT_OK。 * 如果 msg 是 { @link #MSG_DOWNLOAD_IMAGE_URL_FAIL } 发出一条 toast 告诉用户下载失败。 */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (progress != null) { progress.dismiss(); } String toastMessage; switch (msg.what) { case MSG_DOWNLOAD_PICTURE_SUCCEED: setResult(RESULT_OK, null); onBackPressed(); return; case MSG_DOWNLOAD_PICTURE_FAIL: toastMessage = getResources().getString(R.string.toast_picture_source_not_exist); break; case MSG_DOWNLOAD_IMAGE_URL_FAIL: if (mSwipeRefreshLayout.isRefreshing()) mSwipeRefreshLayout.setRefreshing(false); toastMessage = getResources().getString(R.string.toast_download_fail); break; case MSG_NO_RESULT_FOUND_OR_DECODE_FAIL: if (mSwipeRefreshLayout.isRefreshing()) mSwipeRefreshLayout.setRefreshing(false); toastMessage = getResources().getString(R.string.toast_no_result_found_or_decode_fail); break; default: return; } Snackbar.make(mCoordinatorLayout, toastMessage, Snackbar.LENGTH_LONG).setAction("Retry", new View.OnClickListener() { @Override public void onClick(View v) { if (adapter.getList().size() > 0) { onFetchMorePictures(nextPage); } else { executeDownloadTask(MODE_DOWNLOAD_REPLACE, null); } } }).show(); } }; /** * New thread to download picture from @param url to external storage and show a progress. * When download succeed send a { @link #MSG_DOWNLOAD_PICTURE_SUCCEED } message. * 新建一个线程下载 @param url 中的图片到手机扩展储存,并现实一个进度条。 * 新建一个线程下载 url(传入参数) 中的图片到手机扩展储存,并出现一个进度条。 * 当下载成功时,发送一个 { @link #MSG_DOWNLOAD_PICTURE_SUCCEED } message 。 * @param url the url of picture when it was clicked. 被点击的图片的url */ private void downloadImage(final String url) { progress = new ProgressDialog(FetchPictureActivity.this); progress.setMessage("Saving picture"); progress.show(); new Thread(new Runnable() { @Override public void run() { Message msg = new Message(); try { StorageUtils.saveImage(FetchPictureActivity.this, mSaveName + ".jpg", url); msg.what = MSG_DOWNLOAD_PICTURE_SUCCEED; } catch (Exception e) { msg.what = MSG_DOWNLOAD_PICTURE_FAIL; } finally { handler.sendMessage(msg); } } }).start(); } protected static final int MODE_DOWNLOAD_REPLACE = 0; protected static final int MODE_DOWNLOAD_ADD = 1; // ===== views and widgets ===== // ----- Root CoordinatorLayout ----- private CoordinatorLayout mCoordinatorLayout; // ----- RecyclerView ----- private RecyclerView mRecyclerView; // ----- RecyclerAdapter ----- private FPAdapter adapter; // ----- SwipeRefreshLayout ----- private SwipeRefreshLayout mSwipeRefreshLayout; // ----- Toolbar ----- private Toolbar mToolbar; private int nextPage = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.recycler_swipe_refresh); mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.fetch_picture_root_view); if (mQueryKeyword == null) { mQueryKeyword = getIntent().getStringExtra(EXTRA_QUERY_KEYWORD); mSaveName = getIntent().getStringExtra(EXTRA_SAVE_NAME); } mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout); mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { executeDownloadTask(MODE_DOWNLOAD_REPLACE); } }); mSwipeRefreshLayout.setColorSchemeResources(R.color.air_accent_color); // ===== setup toolbar ===== mToolbar = (Toolbar) findViewById(R.id.collapsing_toolbar); // ----- toolbar title ----- mToolbar.setTitle(mQueryKeyword); // ----- toolbar navigation button ----- mToolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha); mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); // ----- toolbar menu search action ----- mToolbar.inflateMenu(R.menu.menu_search); mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (item.getItemId() == R.id.action_search) { SearchManager manager = (SearchManager) getSystemService(SEARCH_SERVICE); SearchView searchView = (SearchView) item.getActionView(); searchView.setSearchableInfo(manager.getSearchableInfo(getComponentName())); ((EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text)) .setHintTextColor(getResources().getColor(R.color.air_text_and_icon)); return true; } return false; } }); // ===== RecyclerView ===== // ----- setup RecyclerView ----- mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); final GridLayoutManager manager = new GridLayoutManager(this, 2); // ----- LayoutManager and Adapter ----- manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return position == 0 ? manager.getSpanCount() : 1; } }); mRecyclerView.setLayoutManager(manager); setupAdapter(); // ----- ScrollListener ----- mRecyclerView.addOnScrollListener(new EasyRecyclerViewListener() { @Override public void onScrollToBottom() { onFetchMorePictures(nextPage); nextPage++; } }); } @Override protected void onResume() { super.onResume(); executeDownloadTask(MODE_DOWNLOAD_ADD); } @Override protected void onNewIntent(Intent intent) { if (intent.getAction().equals(Intent.ACTION_SEARCH)) { mQueryKeyword = intent.getStringExtra(SearchManager.QUERY); mToolbar.setTitle(mQueryKeyword); } executeDownloadTask(MODE_DOWNLOAD_REPLACE); } /** * Adapter of RecyclerView has a head padding, two columns and every item is a { @link #SimpleDraweeView } * RecyclerView 的 adapter 头部有一部分空白填充,两列每一项都是一个 { @link #SimpleDraweeView } */ private class FPAdapter extends AirAdapter { public FPAdapter(Context context, List<?> list) { super(context, list); } @Override public AirItemViewHolder onCreateItemViewHolder(ViewGroup parent) { return new ResponseItemViewHolder(getLayoutInflater() .inflate(R.layout.recycler_item_response, parent, false)); } @Override public void onBindItemViewHolder(AirItemViewHolder itemHolder, int position) { if (itemHolder instanceof ResponseItemViewHolder) { ResponseItemViewHolder item = (ResponseItemViewHolder) itemHolder; item.image.setImageURI(Uri.parse(((Picture)getList().get(position - 1)).getThumbUrl())); } } private class ResponseItemViewHolder extends AirItemViewHolder { SimpleDraweeView image; public ResponseItemViewHolder(View itemView) { super(itemView); image = (SimpleDraweeView) itemView.findViewById(R.id.simple_drawee_view_item); } } } protected void executeDownloadTask(int downloadMod) { this.executeDownloadTask(downloadMod, null); } protected void executeDownloadTask(final int downloadMod, String otherParam) { if (!mSwipeRefreshLayout.isRefreshing()) { mSwipeRefreshLayout.setRefreshing(true); } DownloadURLTask task = new DownloadURLTask() { @Override public String getUrl() { return getSearchLink(); } @Override public ArrayList<Picture> decodeJson(String response) { return onDecodeJson(response); } @Override public void onError(Exception e) { sendErrorMessage(MSG_DOWNLOAD_IMAGE_URL_FAIL); } @Override public void onFinish(ArrayList<Picture> list) { if (list == null) { sendErrorMessage(MSG_NO_RESULT_FOUND_OR_DECODE_FAIL); return; } switch (downloadMod) { case MODE_DOWNLOAD_ADD: for (Picture p : list) { mPictureList.add(p); } adapter.notifyDataSetChanged(); break; case MODE_DOWNLOAD_REPLACE: mPictureList = list; setupAdapter(); nextPage = 2; break; default: break; } mSwipeRefreshLayout.setRefreshing(false); } }; task.execute(StringUtils.encodeKeyword(mQueryKeyword), otherParam); } /** * A convenient method to setup or update data of adapter of RecyclerView for calling more than once. * 一个封装好的简易方法来配置 RecyclerView adapter,以便多次调用 */ private void setupAdapter() { adapter = new FPAdapter(FetchPictureActivity.this, mPictureList); adapter.setOnItemClickListener(new AirAdapter.OnItemClickListener() { @Override public void onItemClicked(View view, final int position) { ReplacePicDialogFragment dialog = new ReplacePicDialogFragment() { @Override public void onOkClick(View view) { downloadImage(mPictureList.get(position - 1).getObjUrl()); dismiss(); } }; dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0); dialog.show(getSupportFragmentManager(), null); } }); mRecyclerView.setAdapter(adapter); } /** * package method to send error message for calling more than once * 封装发送错误信息的方法以便多次调用 */ private void sendErrorMessage(int what) { Message msg = new Message(); msg.what = what; handler.sendMessage(msg); } public abstract String getSearchLink(); public abstract ArrayList<Picture> onDecodeJson(String response); public abstract void onFetchMorePictures(int nextPage); }