package com.wangdaye.mysplash.common.ui.activity; import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.SystemClock; import android.support.design.widget.CoordinatorLayout; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; import com.wangdaye.mysplash.R; import com.wangdaye.mysplash.common._basic.ReadWriteActivity; import com.wangdaye.mysplash.common.data.entity.item.DownloadMission; import com.wangdaye.mysplash.common.ui.dialog.PathDialog; import com.wangdaye.mysplash.common.ui.widget.SwipeBackCoordinatorLayout; import com.wangdaye.mysplash.common.utils.DisplayUtils; import com.wangdaye.mysplash.common.utils.helper.IntentHelper; import com.wangdaye.mysplash.common.utils.helper.DownloadHelper; import com.wangdaye.mysplash.common.data.entity.table.DownloadMissionEntity; import com.wangdaye.mysplash.common.ui.adapter.DownloadAdapter; import com.wangdaye.mysplash.common.ui.widget.coordinatorView.StatusBarView; import com.wangdaye.mysplash.common.utils.manager.ThemeManager; import com.wangdaye.mysplash.common.utils.manager.ThreadManager; import com.wangdaye.mysplash.common.utils.widget.SafeHandler; import com.wangdaye.mysplash.common._basic.FlagRunnable; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; /** * Download manage activity. * * This activity is used to show and manage download missions. * * */ public class DownloadManageActivity extends ReadWriteActivity implements View.OnClickListener, Toolbar.OnMenuItemClickListener, DownloadAdapter.OnRetryListener, SwipeBackCoordinatorLayout.OnSwipeListener, SafeHandler.HandlerContainer { @BindView(R.id.activity_download_manage_container) CoordinatorLayout container; @BindView(R.id.activity_download_manage_statusBar) StatusBarView statusBar; @BindView(R.id.activity_download_manage_recyclerView) RecyclerView recyclerView; private SafeHandler<DownloadManageActivity> handler; private DownloadAdapter adapter; // if we need to restart a mission, we need save it by this object and request permission. private DownloadMissionEntity readyToDownloadEntity; public static final String ACTION_DOWNLOAD_MANAGER = "com.wangdaye.mysplash.DownloadManager"; // we can get a boolean object from intent by using this string as a key. // If is true, that means this activity was opened by click downloading notification. public static final String EXTRA_NOTIFICATION = "notification"; private final int CHECK_AND_UPDATE = 1; /** * This Runnable class is used to poll download progress. * */ private FlagRunnable checkRunnable = new FlagRunnable(true) { @Override public void run() { while (isRunning()) { for (int i = 0; isRunning() && i < adapter.itemList.size(); i ++) { if (adapter.itemList.get(i).entity.result == DownloadHelper.RESULT_DOWNLOADING) { DownloadMission mission = DownloadHelper.getInstance(DownloadManageActivity.this) .getDownloadMission( DownloadManageActivity.this, adapter.itemList.get(i).entity.missionId); if (mission != null && (mission.entity.result == DownloadHelper.RESULT_DOWNLOADING || mission.entity.result != adapter.itemList.get(i).entity.result)) { // only if the state of mission has changed or the progress changed, // then we should send a message to update the item view. handler.obtainMessage(CHECK_AND_UPDATE, mission).sendToTarget(); } SystemClock.sleep(50); } } SystemClock.sleep(50); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_download_mange); } @Override protected void onStart() { super.onStart(); if (!isStarted()) { setStarted(); ButterKnife.bind(this); initData(); initWidget(); checkRunnable.setRunning(true); ThreadManager.getInstance().execute(checkRunnable); } } @Override protected void onDestroy() { super.onDestroy(); checkRunnable.setRunning(false); handler.removeCallbacksAndMessages(null); } @Override protected void setTheme() { if (ThemeManager.getInstance(this).isLightTheme()) { setTheme(R.style.MysplashTheme_light_Translucent_Common); } else { setTheme(R.style.MysplashTheme_dark_Translucent_Common); } } @Override public void handleBackPressed() { finishActivity(SwipeBackCoordinatorLayout.DOWN_DIR); } @Override protected void backToTop() { // do nothing. } @Override public void finishActivity(int dir) { finish(); switch (dir) { case SwipeBackCoordinatorLayout.UP_DIR: overridePendingTransition(0, R.anim.activity_slide_out_top); break; case SwipeBackCoordinatorLayout.DOWN_DIR: overridePendingTransition(0, R.anim.activity_slide_out_bottom); break; } } @Override public CoordinatorLayout getSnackbarContainer() { return container; } // init. private void initData() { this.adapter = new DownloadAdapter(this, this); } private void initWidget() { this.handler = new SafeHandler<>(this); SwipeBackCoordinatorLayout swipeBackView = ButterKnife.findById( this, R.id.activity_download_manage_swipeBackView); swipeBackView.setOnSwipeListener(this); boolean openByNotification = getIntent().getBooleanExtra(EXTRA_NOTIFICATION, false); Toolbar toolbar = ButterKnife.findById(this, R.id.activity_download_manage_toolbar); if (openByNotification) { ThemeManager.setNavigationIcon( toolbar, R.drawable.ic_toolbar_home_light, R.drawable.ic_toolbar_home_dark); } else { ThemeManager.setNavigationIcon( toolbar, R.drawable.ic_toolbar_back_light, R.drawable.ic_toolbar_back_dark); } ThemeManager.inflateMenu(toolbar, R.menu.activity_download_manage_toolbar_light, R.menu.activity_download_manage_toolbar_dark); toolbar.setNavigationOnClickListener(this); toolbar.setOnMenuItemClickListener(this); recyclerView.setLayoutManager( new GridLayoutManager(this, DisplayUtils.getGirdColumnCount(this))); recyclerView.setAdapter(adapter); } // control. /** * Make item view show downloading progress and percent. * * @param position Adapter position for item. * @param mission A {@link DownloadMission} object which saved information of downloading task. * @param switchState If set true, that means the item view will switch from another state to * the downloading state. * */ private void drawRecyclerItemProcess(int position, DownloadMission mission, boolean switchState) { adapter.itemList.set(position, mission); GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager(); int firstPosition = layoutManager.findFirstVisibleItemPosition(); int lastPosition = layoutManager.findLastVisibleItemPosition(); if (firstPosition <= position && position <= lastPosition) { // we doesn't need to refresh a item view that is not displayed. DownloadAdapter.ViewHolder holder = (DownloadAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(position); if (holder != null) { holder.drawProcessStatus(mission, switchState); } } } /** * Make the item view to show the information that means "Download successful". * * @param position Adapter position for item. * @param mission A {@link DownloadMission} object which saved information of downloading task. * */ private void drawRecyclerItemSucceed(int position, DownloadMission mission) { adapter.itemList.remove(position); adapter.notifyItemRemoved(position); for (int i = adapter.itemList.size() - 1; i >= 0; i --) { if (adapter.itemList.get(i).entity.result != DownloadHelper.RESULT_SUCCEED) { adapter.itemList.add(i + 1, mission); adapter.notifyItemInserted(i + 1); return; } } adapter.itemList.add(0, mission); adapter.notifyItemInserted(0); } /** * Make the item view to show the information that means "Download failed". * * @param position Adapter position for item. * @param mission A {@link DownloadMission} object which saved information of downloading task. * */ private void drawRecyclerItemFailed(int position, DownloadMission mission) { // remove the old item and add a new item on the first position of list. adapter.itemList.remove(position); adapter.notifyItemRemoved(position); adapter.itemList.add(0, mission); adapter.notifyItemInserted(0); } private void restartMission() { if (readyToDownloadEntity == null) { return; } long oldId = readyToDownloadEntity.missionId; readyToDownloadEntity = null; DownloadMission mission = DownloadHelper.getInstance(this).restartMission(this, oldId); // remove the old item. for (int i = 0; i < adapter.itemList.size(); i ++) { if (adapter.itemList.get(i).entity.missionId == oldId) { adapter.itemList.remove(i); adapter.notifyItemRemoved(i); break; } } // add the new item. if (adapter.itemList.size() > 0) { // if the list's size > 0, we need find the last failed mission item and add the new item after it. for (int i = 0; i < adapter.itemList.size(); i ++) { if (adapter.itemList.get(i).entity.result != DownloadHelper.RESULT_FAILED) { adapter.itemList.add(i, mission); adapter.notifyItemInserted(i); return; } } adapter.itemList.add(adapter.itemList.size(), mission); adapter.notifyItemInserted(adapter.itemList.size() - 1); } else { adapter.itemList.add(0, mission); adapter.notifyItemInserted(0); } } // permission. @Override protected void requestReadWritePermissionSucceed(int requestCode) { restartMission(); } // interface. // on click listener. @Override public void onClick(View view) { switch (view.getId()) { case -1: if (getIntent().getBooleanExtra(EXTRA_NOTIFICATION, false)) { IntentHelper.startMainActivity(this); } finishActivity(SwipeBackCoordinatorLayout.DOWN_DIR); break; } } // on menu item click listener. @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_path: PathDialog dialog = new PathDialog(); dialog.show(getFragmentManager(), null); break; case R.id.action_cancel_all: List<DownloadMissionEntity> entityList = new ArrayList<>(); for (int i = 0; i < adapter.getItemCount(); i ++) { entityList.add(adapter.itemList.get(i).entity); } DownloadHelper.getInstance(this).clearMission(this, entityList); adapter.itemList.clear(); adapter.notifyDataSetChanged(); break; } return true; } // on retry listener. @Override public void onRetry(DownloadMissionEntity entity) { readyToDownloadEntity = entity; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { restartMission(); } else { requestReadWritePermission(); } } // on swipe listener. @Override public boolean canSwipeBack(int dir) { return SwipeBackCoordinatorLayout.canSwipeBack(recyclerView, dir); } @Override public void onSwipeProcess(float percent) { statusBar.setAlpha(1 - percent); container.setBackgroundColor(SwipeBackCoordinatorLayout.getBackgroundColor(percent)); } @Override public void onSwipeFinish(int dir) { finishActivity(dir); } // handler. @Override public void handleMessage(Message message) { switch (message.what) { case CHECK_AND_UPDATE: if (message.obj != null && message.obj instanceof DownloadMission) { DownloadMission newMission = (DownloadMission) message.obj; // try to find the mission's position. int index = -1; for (int i = 0; i < adapter.getItemCount(); i ++) { if (adapter.itemList.get(i).entity.missionId == newMission.entity.missionId) { index = i; break; } } if (index == -1) { // cannot find the mission's position. return; } DownloadMission oldMission = adapter.itemList.get(index); switch (newMission.entity.result) { case DownloadHelper.RESULT_DOWNLOADING: if (oldMission.entity.result != DownloadHelper.RESULT_DOWNLOADING) { DownloadHelper.getInstance(this) .updateMissionResult( this, newMission.entity.missionId, DownloadHelper.RESULT_DOWNLOADING); drawRecyclerItemProcess(index, newMission, true); } else if (oldMission.process != newMission.process) { drawRecyclerItemProcess(index, newMission, false); } break; case DownloadHelper.RESULT_SUCCEED: if (oldMission.entity.result != DownloadHelper.RESULT_SUCCEED) { DownloadHelper.getInstance(this) .updateMissionResult( this, newMission.entity.missionId, DownloadHelper.RESULT_SUCCEED); drawRecyclerItemSucceed(index, newMission); } break; case DownloadHelper.RESULT_FAILED: if (oldMission.entity.result != DownloadHelper.RESULT_FAILED) { DownloadHelper.getInstance(this) .updateMissionResult( this, newMission.entity.missionId, DownloadHelper.RESULT_FAILED); drawRecyclerItemFailed(index, newMission); } break; } } break; } } }