package com.yzy.supercleanmaster.service; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import android.os.AsyncTask; import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.StatFs; import android.text.format.Formatter; import android.util.Log; import android.widget.Toast; import com.yzy.supercleanmaster.R; import com.yzy.supercleanmaster.model.CacheListItem; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; public class CleanerService extends Service { public static final String ACTION_CLEAN_AND_EXIT = "com.yzy.cache.cleaner.CLEAN_AND_EXIT"; private static final String TAG = "CleanerService"; private Method mGetPackageSizeInfoMethod, mFreeStorageAndNotifyMethod; private OnActionListener mOnActionListener; private boolean mIsScanning = false; private boolean mIsCleaning = false; private long mCacheSize = 0; public static interface OnActionListener { public void onScanStarted(Context context); public void onScanProgressUpdated(Context context, int current, int max); public void onScanCompleted(Context context, List<CacheListItem> apps); public void onCleanStarted(Context context); public void onCleanCompleted(Context context, long cacheSize); } public class CleanerServiceBinder extends Binder { public CleanerService getService() { return CleanerService.this; } } private CleanerServiceBinder mBinder = new CleanerServiceBinder(); private class TaskScan extends AsyncTask<Void, Integer, List<CacheListItem>> { private int mAppCount = 0; @Override protected void onPreExecute() { if (mOnActionListener != null) { mOnActionListener.onScanStarted(CleanerService.this); } } @Override protected List<CacheListItem> doInBackground(Void... params) { mCacheSize = 0; final List<ApplicationInfo> packages = getPackageManager().getInstalledApplications( PackageManager.GET_META_DATA); publishProgress(0, packages.size()); final CountDownLatch countDownLatch = new CountDownLatch(packages.size()); final List<CacheListItem> apps = new ArrayList<CacheListItem>(); try { for (ApplicationInfo pkg : packages) { mGetPackageSizeInfoMethod.invoke(getPackageManager(), pkg.packageName, new IPackageStatsObserver.Stub() { @Override public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException { synchronized (apps) { publishProgress(++mAppCount, packages.size()); if (succeeded && pStats.cacheSize > 0) { try { apps.add(new CacheListItem(pStats.packageName, getPackageManager().getApplicationLabel( getPackageManager().getApplicationInfo( pStats.packageName, PackageManager.GET_META_DATA) ).toString(), getPackageManager().getApplicationIcon( pStats.packageName), pStats.cacheSize )); mCacheSize += pStats.cacheSize; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } } synchronized (countDownLatch) { countDownLatch.countDown(); } } } ); } countDownLatch.await(); } catch (InvocationTargetException | InterruptedException | IllegalAccessException e) { e.printStackTrace(); } return new ArrayList<>(apps); } @Override protected void onProgressUpdate(Integer... values) { if (mOnActionListener != null) { mOnActionListener.onScanProgressUpdated(CleanerService.this, values[0], values[1]); } } @Override protected void onPostExecute(List<CacheListItem> result) { if (mOnActionListener != null) { mOnActionListener.onScanCompleted(CleanerService.this, result); } mIsScanning = false; } } private class TaskClean extends AsyncTask<Void, Void, Long> { @Override protected void onPreExecute() { if (mOnActionListener != null) { mOnActionListener.onCleanStarted(CleanerService.this); } } @Override protected Long doInBackground(Void... params) { final CountDownLatch countDownLatch = new CountDownLatch(1); StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath()); try { mFreeStorageAndNotifyMethod.invoke(getPackageManager(), (long) stat.getBlockCount() * (long) stat.getBlockSize(), new IPackageDataObserver.Stub() { @Override public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException { countDownLatch.countDown(); } } ); countDownLatch.await(); } catch (InvocationTargetException | InterruptedException | IllegalAccessException e) { e.printStackTrace(); } return mCacheSize; } @Override protected void onPostExecute(Long result) { mCacheSize = 0; if (mOnActionListener != null) { mOnActionListener.onCleanCompleted(CleanerService.this, result); } mIsCleaning = false; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { try { mGetPackageSizeInfoMethod = getPackageManager().getClass().getMethod( "getPackageSizeInfo", String.class, IPackageStatsObserver.class); mFreeStorageAndNotifyMethod = getPackageManager().getClass().getMethod( "freeStorageAndNotify", long.class, IPackageDataObserver.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); if (action != null) { if (action.equals(ACTION_CLEAN_AND_EXIT)) { setOnActionListener(new OnActionListener() { @Override public void onScanStarted(Context context) { } @Override public void onScanProgressUpdated(Context context, int current, int max) { } @Override public void onScanCompleted(Context context, List<CacheListItem> apps) { if (getCacheSize() > 0) { cleanCache(); } } @Override public void onCleanStarted(Context context) { } @Override public void onCleanCompleted(Context context, long cacheSize) { String msg = getString(R.string.cleaned, Formatter.formatShortFileSize( CleanerService.this, cacheSize)); Log.d(TAG, msg); Toast.makeText(CleanerService.this, msg, Toast.LENGTH_LONG).show(); new Handler().postDelayed(new Runnable() { @Override public void run() { stopSelf(); } }, 5000); } }); scanCache(); } } return START_NOT_STICKY; } public void scanCache() { mIsScanning = true; new TaskScan().execute(); } public void cleanCache() { mIsCleaning = true; new TaskClean().execute(); } public void setOnActionListener(OnActionListener listener) { mOnActionListener = listener; } public boolean isScanning() { return mIsScanning; } public boolean isCleaning() { return mIsCleaning; } public long getCacheSize() { return mCacheSize; } }