diff --git a/CacheCleaner/build.gradle b/CacheCleaner/build.gradle index 38ba063..d595e79 100644 --- a/CacheCleaner/build.gradle +++ b/CacheCleaner/build.gradle @@ -3,9 +3,14 @@ import org.ajoberstar.grgit.Grgit buildscript { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' +// classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:3.1.2' classpath 'org.ajoberstar:gradle-git:1.4.2' } } @@ -13,6 +18,10 @@ apply plugin: 'com.android.application' repositories { mavenCentral() + maven { + url 'https://maven.google.com/' + name 'Google' + } } ext { @@ -22,13 +31,13 @@ ext { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 27 + buildToolsVersion '27.0.3' defaultConfig { applicationId "com.frozendevs.cache.cleaner" - minSdkVersion 9 - targetSdkVersion 23 + minSdkVersion 15 + targetSdkVersion 27 versionCode = gitVersionCode versionName = gitVersionName @@ -36,8 +45,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } buildTypes { @@ -57,10 +66,10 @@ android { } dependencies { - compile('com.android.support:support-v4:23.4.0') - compile('com.android.support:appcompat-v7:23.4.0@aar') - compile('com.android.support:recyclerview-v7:23.4.0@aar') - compile('com.android.support:support-vector-drawable:23.4.0@aar') + implementation 'com.android.support:support-v4:27.1.1' + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support:recyclerview-v7:27.1.1' + implementation 'com.android.support:support-vector-drawable:27.1.1' } if (project.hasProperty("CacheCleaner.signing") && diff --git a/CacheCleaner/src/main/AndroidManifest.xml b/CacheCleaner/src/main/AndroidManifest.xml index d3f142c..9a7e3a8 100644 --- a/CacheCleaner/src/main/AndroidManifest.xml +++ b/CacheCleaner/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -8,6 +9,10 @@ + + stats = null; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { + mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + if (mUsageStatsManager != null) { + stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, now - 1000 * 10, now); + } + } + + return (stats != null && !stats.isEmpty()); + } } diff --git a/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/fragment/CleanerFragment.java b/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/fragment/CleanerFragment.java index cad0a32..28ac7be 100644 --- a/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/fragment/CleanerFragment.java +++ b/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/fragment/CleanerFragment.java @@ -1,6 +1,8 @@ package com.frozendevs.cache.cleaner.fragment; import android.Manifest; +import android.annotation.TargetApi; +import android.app.Activity; import android.app.ProgressDialog; import android.content.ComponentName; import android.content.Context; @@ -15,6 +17,7 @@ import android.os.IBinder; import android.os.StatFs; import android.preference.PreferenceManager; +import android.provider.Settings; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; @@ -34,6 +37,7 @@ import android.widget.Toast; import com.frozendevs.cache.cleaner.R; +import com.frozendevs.cache.cleaner.activity.CleanerActivity; import com.frozendevs.cache.cleaner.activity.SettingsActivity; import com.frozendevs.cache.cleaner.model.AppsListItem; import com.frozendevs.cache.cleaner.model.CleanerService; @@ -43,6 +47,8 @@ import java.util.List; +import static com.frozendevs.cache.cleaner.activity.CleanerActivity.USAGE_ACCESS_SETTINGS; + public class CleanerFragment extends Fragment implements CleanerService.OnActionListener { private static final int REQUEST_STORAGE = 0; @@ -96,10 +102,10 @@ public void onServiceDisconnected(ComponentName name) { } }; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); setRetainInstance(true); @@ -123,6 +129,59 @@ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { } }); + startService(); + } + + + void startService() { + + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + + boolean usageStats = CleanerActivity.checkUsageStatsEnabled(getActivity()); + + if (usageStats) { + + startCleanerService(); + + } else { + + showDialog(getActivity(), "Usage Access Settings", "You need to allow Usage Access Settings to access cache of all app."); + } + } else { + startCleanerService(); + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void showDialog(Activity activity, String title, CharSequence message) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + + if (title != null) builder.setTitle(title); + + builder.setMessage(message); + builder.setPositiveButton("Allow", new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialogInterface, int i) { + + Intent usageIntent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); + startActivityForResult(usageIntent, USAGE_ACCESS_SETTINGS); + } + }); + builder.setCancelable(false); + builder.show(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == USAGE_ACCESS_SETTINGS) + startService(); + + } + + private void startCleanerService() { getActivity().getApplication().bindService(new Intent(getActivity(), CleanerService.class), mServiceConnection, Context.BIND_AUTO_CREATE); } diff --git a/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/model/CleanerService.java b/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/model/CleanerService.java index cac10c1..227bac6 100644 --- a/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/model/CleanerService.java +++ b/CacheCleaner/src/main/java/com/frozendevs/cache/cleaner/model/CleanerService.java @@ -2,6 +2,8 @@ import android.Manifest; import android.app.Service; +import android.app.usage.StorageStats; +import android.app.usage.StorageStatsManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -17,6 +19,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.StatFs; +import android.os.storage.StorageManager; import android.support.v4.content.ContextCompat; import android.util.Log; import android.widget.Toast; @@ -30,6 +33,8 @@ import java.util.List; import java.util.concurrent.CountDownLatch; +import static android.content.pm.PackageManager.GET_META_DATA; + public class CleanerService extends Service { public static final String ACTION_CLEAN_AND_EXIT = "com.frozendevs.cache.cleaner.CLEAN_AND_EXIT"; @@ -83,36 +88,69 @@ protected List doInBackground(Void... params) { publishProgress(0, packages.size()); - final CountDownLatch countDownLatch = new CountDownLatch(packages.size()); final List apps = new ArrayList<>(); - try { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + + final StorageStatsManager storageStatsManager = (StorageStatsManager) getSystemService(Context.STORAGE_STATS_SERVICE); + + final StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); + 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()); + try { - mCacheSize += addPackage(apps, pStats, succeeded); - } + ApplicationInfo info = getPackageManager().getApplicationInfo(pkg.packageName, GET_META_DATA); + StorageStats storageStats = storageStatsManager.queryStatsForUid(info.storageUuid, info.uid); + + publishProgress(++mAppCount, packages.size()); + long cacheSize = storageStats.getCacheBytes(); + mCacheSize += cacheSize; + + try { + PackageManager packageManager = getPackageManager(); + + apps.add(new AppsListItem(pkg.packageName, + packageManager.getApplicationLabel(info).toString(), + packageManager.getApplicationIcon(pkg.packageName), + cacheSize)); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } - synchronized (countDownLatch) { - countDownLatch.countDown(); + } catch (Exception ignored) { + } + } + } else { + final CountDownLatch countDownLatch = new CountDownLatch(packages.size()); + 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()); + + mCacheSize += addPackage(apps, pStats, succeeded); + } + + synchronized (countDownLatch) { + countDownLatch.countDown(); + } } } - } - ); - } + ); + } - countDownLatch.await(); - } catch (InvocationTargetException | InterruptedException | IllegalAccessException e) { - e.printStackTrace(); + countDownLatch.await(); + } catch (InvocationTargetException | InterruptedException | IllegalAccessException e) { + e.printStackTrace(); + } } return new ArrayList<>(apps); @@ -339,8 +377,7 @@ public void onCleanStarted(Context context) { public void onCleanCompleted(Context context, boolean succeeded) { if (succeeded) { Log.d(TAG, "Cache cleaned"); - } - else { + } else { Log.e(TAG, "Could not clean the cache"); } diff --git a/build.gradle b/build.gradle index 495c503..02ffbad 100644 --- a/build.gradle +++ b/build.gradle @@ -1 +1,10 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + maven { + url 'https://maven.google.com/' + name 'Google' + } + } +}