/* * Copyright (C) 2014 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com> * * This file is part of Amaze File Manager. * * Amaze File Manager is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.amaze.filemanager.fragments; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v7.widget.CardView; import android.text.Html; import android.text.Spanned; import android.text.format.Formatter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.amaze.filemanager.R; import com.amaze.filemanager.activities.MainActivity; import com.amaze.filemanager.services.CopyService; import com.amaze.filemanager.services.EncryptService; import com.amaze.filemanager.services.ExtractService; import com.amaze.filemanager.services.ZipTask; import com.amaze.filemanager.ui.icons.IconUtils; import com.amaze.filemanager.utils.DataPackage; import com.amaze.filemanager.utils.Futils; import com.amaze.filemanager.utils.Utils; import com.amaze.filemanager.utils.color.ColorUsage; import com.amaze.filemanager.utils.theme.AppTheme; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import java.util.ArrayList; import java.util.concurrent.TimeUnit; public class ProcessViewer extends Fragment { boolean isInitialized = false; SharedPreferences sharedPrefs; IconUtils icons; MainActivity mainActivity; int accentColor, primaryColor; ImageButton mCancelButton; ImageView mProgressImage; private View rootView; private CardView mCardView; private LineChart mLineChart; private LineData mLineData = new LineData(); private long time = 0L; private TextView mProgressTypeText, mProgressFileNameText, mProgressBytesText, mProgressFileText, mProgressSpeedText, mProgressTimer; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.processparent, container, false); setRetainInstance(false); mainActivity = (MainActivity) getActivity(); accentColor = mainActivity.getColorPreference().getColor(ColorUsage.ACCENT); primaryColor = mainActivity.getColorPreference().getColor(ColorUsage.getPrimary(MainActivity.currentTab)); if (mainActivity.getAppTheme().equals(AppTheme.DARK)) rootView.setBackgroundResource((R.color.cardView_background)); mainActivity.updateViews(new ColorDrawable(primaryColor)); mainActivity.setActionBarTitle(getResources().getString(R.string.process_viewer)); mainActivity.floatingActionButton.hideMenuButton(true); sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); icons = new IconUtils(sharedPrefs, getActivity()); mainActivity.supportInvalidateOptionsMenu(); mCardView = (CardView) rootView.findViewById(R.id.card_view); mLineChart = (LineChart) rootView.findViewById(R.id.progress_chart); mProgressImage = (ImageView) rootView.findViewById(R.id.progress_image); mCancelButton = (ImageButton) rootView.findViewById(R.id.delete_button); mProgressTypeText = (TextView) rootView.findViewById(R.id.text_view_progress_type); mProgressFileNameText = (TextView) rootView.findViewById(R.id.text_view_progress_file_name); mProgressBytesText = (TextView) rootView.findViewById(R.id.text_view_progress_bytes); mProgressFileText = (TextView) rootView.findViewById(R.id.text_view_progress_file); mProgressSpeedText = (TextView) rootView.findViewById(R.id.text_view_progress_speed); mProgressTimer = (TextView) rootView.findViewById(R.id.text_view_progress_timer); if (mainActivity.getAppTheme().equals(AppTheme.DARK)) { mCancelButton.setImageResource(R.drawable.ic_action_cancel); mCardView.setCardBackgroundColor(Utils.getColor(getContext(), R.color.cardView_foreground)); mCardView.setCardElevation(0f); } return rootView; } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance CopyService.LocalBinder localBinder = (CopyService.LocalBinder) service; CopyService copyService = localBinder.getService(); for (int i=0; i<copyService.getDataPackageSize(); i++) { processResults(copyService.getDataPackage(i), ServiceType.COPY); } // animate the chart a little after initial values have been applied mLineChart.animateXY(500, 500); copyService.setProgressListener(new CopyService.ProgressListener() { @Override public void onUpdate(final DataPackage dataPackage) { if (getActivity() == null || getActivity().getSupportFragmentManager(). findFragmentByTag(MainActivity.KEY_INTENT_PROCESS_VIEWER) == null) { // callback called when we're not inside the app return; } getActivity().runOnUiThread(new Runnable() { @Override public void run() { processResults(dataPackage, ServiceType.COPY); } }); } @Override public void refresh() { } }); } @Override public void onServiceDisconnected(ComponentName arg0) { } }; private ServiceConnection mExtractConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance ExtractService.LocalBinder localBinder = (ExtractService.LocalBinder) service; ExtractService extractService = localBinder.getService(); for (int i=0; i<extractService.getDataPackageSize(); i++) { processResults(extractService.getDataPackage(i), ServiceType.EXTRACT); } // animate the chart a little after initial values have been applied mLineChart.animateXY(500, 500); extractService.setProgressListener(new ExtractService.ProgressListener() { @Override public void onUpdate(final DataPackage dataPackage) { if (getActivity()==null) { // callback called when we're not inside the app return; } getActivity().runOnUiThread(new Runnable() { @Override public void run() { processResults(dataPackage, ServiceType.EXTRACT); } }); } @Override public void refresh() { } }); } @Override public void onServiceDisconnected(ComponentName arg0) { } }; private ServiceConnection mCompressConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance ZipTask.LocalBinder localBinder = (ZipTask.LocalBinder) service; ZipTask zipTask = localBinder.getService(); for (int i=0; i<zipTask.getDataPackageSize(); i++) { processResults(zipTask.getDataPackage(i), ServiceType.COMPRESS); } // animate the chart a little after initial values have been applied mLineChart.animateXY(500, 500); zipTask.setProgressListener(new ZipTask.ProgressListener() { @Override public void onUpdate(final DataPackage dataPackage) { if (getActivity() == null) { // callback called when we're not inside the app return; } getActivity().runOnUiThread(new Runnable() { @Override public void run() { processResults(dataPackage, ServiceType.COMPRESS); } }); } @Override public void refresh() { } }); } @Override public void onServiceDisconnected(ComponentName arg0) { } }; private ServiceConnection mCryptConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { EncryptService.LocalBinder binder = (EncryptService.LocalBinder) service; EncryptService encryptService = binder.getService(); for (int i=0; i<encryptService.getDataPackageSize(); i++) { DataPackage dataPackage = encryptService.getDataPackage(i); processResults(dataPackage, dataPackage.isMove() ? ServiceType.DECRYPT : ServiceType.ENCRYPT); } // animate the chart a little after initial values have been applied mLineChart.animateXY(500, 500); encryptService.setProgressListener(new EncryptService.ProgressListener() { @Override public void onUpdate(final DataPackage dataPackage) { if (getActivity() == null) { // callback called when we're not inside the app return; } getActivity().runOnUiThread(new Runnable() { @Override public void run() { processResults(dataPackage, dataPackage.isMove() ? ServiceType.DECRYPT : ServiceType.ENCRYPT); } }); } @Override public void refresh() { } }); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override public void onResume() { super.onResume(); Intent intent = new Intent(getActivity(), CopyService.class); getActivity().bindService(intent, mConnection, 0); Intent intent1 = new Intent(getActivity(), ExtractService.class); getActivity().bindService(intent1, mExtractConnection, 0); Intent intent2 = new Intent(getActivity(), ZipTask.class); getActivity().bindService(intent2, mCompressConnection, 0); Intent intent3 = new Intent(getActivity(), EncryptService.class); getActivity().bindService(intent3, mCryptConnection, 0); } @Override public void onPause() { super.onPause(); getActivity().unbindService(mConnection); getActivity().unbindService(mExtractConnection); getActivity().unbindService(mCompressConnection); getActivity().unbindService(mCryptConnection); } @Override public void onDestroy() { super.onDestroy(); } /** * Enum helps defining the result type for {@link #processResults(DataPackage, ServiceType)} * to process */ enum ServiceType { COPY, EXTRACT, COMPRESS, ENCRYPT, DECRYPT } public void processResults(final DataPackage dataPackage, ServiceType serviceType) { if (dataPackage != null) { String name = dataPackage.getName(); long total = dataPackage.getTotal(); long doneBytes = dataPackage.getByteProgress(); boolean move = dataPackage.isMove(); if (!isInitialized) { // initializing views for the first time chartInit(total); // setting progress image setupDrawables(serviceType, move); isInitialized = true; } addEntry(Futils.readableFileSizeFloat(doneBytes), Futils.readableFileSizeFloat(dataPackage.getSpeedRaw())); mProgressFileNameText.setText(name); Spanned bytesText = Html.fromHtml(getResources().getString(R.string.written) + " <font color='" + accentColor + "'><i>" + Formatter.formatFileSize(getContext(), doneBytes) + " </font></i>" + getResources().getString(R.string.out_of) + " <i>" + Formatter.formatFileSize(getContext(), total) + "</i>"); mProgressBytesText.setText(bytesText); Spanned fileProcessedSpan = Html.fromHtml(getResources().getString(R.string.processing_file) + " <font color='" + accentColor + "'><i>" + (dataPackage.getSourceProgress()) + " </font></i>" + getResources().getString(R.string.of) + " <i>" + dataPackage.getSourceFiles() + "</i>"); mProgressFileText.setText(fileProcessedSpan); Spanned speedSpan = Html.fromHtml(getResources().getString(R.string.current_speed) + ": <font color='" + accentColor + "'><i>" + Formatter.formatFileSize(getContext(), dataPackage.getSpeedRaw()) + "/s</font></i>"); mProgressSpeedText.setText(speedSpan); Spanned timerSpan = Html.fromHtml(getResources().getString(R.string.service_timer) + ": <font color='" + accentColor + "'><i>" + formatTimer(++time) + "</font></i>"); mProgressTimer.setText(timerSpan); } } /** * Formats input to plain mm:ss format * @param timer * @return */ private String formatTimer(long timer) { final long min = TimeUnit.SECONDS.toMinutes(timer); final long sec = TimeUnit.SECONDS.toSeconds(timer - TimeUnit.MINUTES.toMillis(min)); return String.format("%02d:%02d", min, sec); } /** * Setup drawables and click listeners based on the {@link ServiceType} * @param serviceType */ private void setupDrawables(ServiceType serviceType, boolean isMove) { switch (serviceType) { case COPY: if (mainActivity.getAppTheme().equals(AppTheme.DARK)) { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_content_copy_white_36dp)); } else { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_content_copy_grey600_36dp)); } mProgressTypeText.setText(isMove ? getResources().getString(R.string.moving) : getResources().getString(R.string.copying)); cancelBroadcast(new Intent(CopyService.TAG_BROADCAST_COPY_CANCEL)); break; case EXTRACT: if (mainActivity.getAppTheme().equals(AppTheme.DARK)) { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_zip_box_white_36dp)); } else { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_zip_box_grey600_36dp)); } mProgressTypeText.setText(getResources().getString(R.string.extracting)); cancelBroadcast(new Intent(ExtractService.TAG_BROADCAST_EXTRACT_CANCEL)); break; case COMPRESS: if (mainActivity.getAppTheme().equals(AppTheme.DARK)) { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_zip_box_white_36dp)); } else { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_zip_box_grey600_36dp)); } mProgressTypeText.setText(getResources().getString(R.string.compressing)); cancelBroadcast(new Intent(ZipTask.KEY_COMPRESS_BROADCAST_CANCEL)); break; case ENCRYPT: if (mainActivity.getAppTheme().equals(AppTheme.DARK)) { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_folder_lock_white_36dp)); } else { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_folder_lock_grey600_36dp)); } mProgressTypeText.setText(getResources().getString(R.string.crypt_encrypting)); cancelBroadcast(new Intent(EncryptService.TAG_BROADCAST_CRYPT_CANCEL)); break; case DECRYPT: if (mainActivity.getAppTheme().equals(AppTheme.DARK)) { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_folder_lock_open_white_36dp)); } else { mProgressImage.setImageDrawable(getResources() .getDrawable(R.drawable.ic_folder_lock_open_grey600_36dp)); } mProgressTypeText.setText(getResources().getString(R.string.crypt_decrypting)); cancelBroadcast(new Intent(EncryptService.TAG_BROADCAST_CRYPT_CANCEL)); break; } } /** * Setup click listener to cancel button click for various intent types * @param intent */ private void cancelBroadcast(final Intent intent) { mCancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getActivity(), getResources().getString(R.string.stopping), Toast.LENGTH_LONG).show(); getActivity().sendBroadcast(intent); mProgressTypeText.setText(getResources().getString(R.string.cancelled)); mProgressSpeedText.setText(""); mProgressFileText.setText(""); mProgressBytesText.setText(""); mProgressFileNameText.setText(""); mProgressTypeText.setTextColor(Utils.getColor(getContext(), android.R.color.holo_red_light)); } }); } /** * Add a new entry dynamically to the chart, initializes a {@link LineDataSet} if not done so * @param xValue the x-axis value, the number of bytes processed till now * @param yValue the y-axis value, bytes processed per sec */ private void addEntry(float xValue, float yValue) { ILineDataSet dataSet = mLineData.getDataSetByIndex(0); if (dataSet==null) { // adding set for first time dataSet = createDataSet(); mLineData.addDataSet(dataSet); } int randomDataSetIndex = (int) (Math.random() * mLineData.getDataSetCount()); mLineData.addEntry(new Entry(xValue, yValue), randomDataSetIndex); mLineData.notifyDataChanged(); // let the chart know it's data has changed mLineChart.notifyDataSetChanged(); mLineChart.invalidate(); } /** * Creates an instance for {@link LineDataSet} which will store the entries * @return */ private LineDataSet createDataSet() { LineDataSet lineDataset = new LineDataSet(new ArrayList<Entry>(), null); lineDataset.setLineWidth(1.75f); lineDataset.setCircleRadius(5f); lineDataset.setCircleHoleRadius(2.5f); lineDataset.setColor(Color.WHITE); lineDataset.setCircleColor(Color.WHITE); lineDataset.setHighLightColor(Color.WHITE); lineDataset.setDrawValues(false); lineDataset.setCircleColorHole(accentColor); return lineDataset; } /** * Initialize chart for the first time * @param totalBytes maximum value for x-axis */ private void chartInit(long totalBytes) { mLineChart.setBackgroundColor(accentColor); mLineChart.getLegend().setEnabled(false); // no description text mLineChart.getDescription().setEnabled(false); XAxis xAxis = mLineChart.getXAxis(); YAxis yAxisLeft = mLineChart.getAxisLeft(); mLineChart.getAxisRight().setEnabled(false); yAxisLeft.setTextColor(Color.WHITE); yAxisLeft.setAxisLineColor(Color.TRANSPARENT); yAxisLeft.setTypeface(Typeface.DEFAULT_BOLD); yAxisLeft.setGridColor(Utils.getColor(getContext(), R.color.white_translucent)); xAxis.setAxisMaximum(Futils.readableFileSizeFloat(totalBytes)); xAxis.setAxisMinimum(0.0f); xAxis.setAxisLineColor(Color.TRANSPARENT); xAxis.setGridColor(Color.TRANSPARENT); xAxis.setTextColor(Color.WHITE); xAxis.setTypeface(Typeface.DEFAULT_BOLD); mLineChart.setData(mLineData); mLineChart.invalidate(); } }