Merge \\"Remove the Deletion Helper and Automatic Storage Management jobs.\\" into nyc-mr1-dev am: de17410d37
am: 4938a9a470
Change-Id: If4f17948cef697c6bc4cb0306a7506eb445f49d7
This commit is contained in:
@@ -2921,17 +2921,6 @@
|
|||||||
android:value="true" />
|
android:value="true" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="Settings$DeletionHelperActivity"
|
|
||||||
android:label="@string/deletion_helper_title"
|
|
||||||
android:taskAffinity="">
|
|
||||||
<intent-filter android:priority="1">
|
|
||||||
<action android:name="android.settings.DELETION_HELPER_SETTINGS" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
|
||||||
android:value="com.android.settings.deletionhelper.DeletionHelperFragment" />
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<!-- activity for gesture settings -->
|
<!-- activity for gesture settings -->
|
||||||
<activity android:name="Settings$GestureSettingsActivity"
|
<activity android:name="Settings$GestureSettingsActivity"
|
||||||
android:label="@string/gesture_preference_title"
|
android:label="@string/gesture_preference_title"
|
||||||
@@ -3010,21 +2999,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Automatic storage management tasks. -->
|
|
||||||
<service
|
|
||||||
android:name=".deletionhelper.AutomaticStorageManagementJobService"
|
|
||||||
android:label="@string/automatic_storage_manager_service_label"
|
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
|
||||||
android:enabled="@bool/enable_automatic_storage_management"
|
|
||||||
android:exported="false"/>
|
|
||||||
|
|
||||||
<receiver android:name=".deletionhelper.AutomaticStorageBroadcastReceiver"
|
|
||||||
android:enabled="@bool/enable_automatic_storage_management">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<!-- This is the longest AndroidManifest.xml ever. -->
|
<!-- This is the longest AndroidManifest.xml ever. -->
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2016 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="32dp"
|
|
||||||
android:height="32dp"
|
|
||||||
android:viewportWidth="24.0"
|
|
||||||
android:viewportHeight="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
|
|
||||||
</vector>
|
|
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2016 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="32dp"
|
|
||||||
android:height="32dp"
|
|
||||||
android:viewportWidth="24.0"
|
|
||||||
android:viewportHeight="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
|
||||||
</vector>
|
|
@@ -41,6 +41,6 @@
|
|||||||
<!-- Whether none security option is hide or not (country specific). -->
|
<!-- Whether none security option is hide or not (country specific). -->
|
||||||
<bool name="config_hide_none_security_option">false</bool>
|
<bool name="config_hide_none_security_option">false</bool>
|
||||||
|
|
||||||
<!-- Whether the automatic storage management job should be scheduled. -->
|
<!--Whether the storage manager exists. -->
|
||||||
<bool name="enable_automatic_storage_management">false</bool>
|
<bool name="config_has_storage_manager">false</bool>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -7493,17 +7493,6 @@
|
|||||||
<!-- [CHAR LIMIT=60] Name of dev option called demo mode -->
|
<!-- [CHAR LIMIT=60] Name of dev option called demo mode -->
|
||||||
<string name="demo_mode">Demo mode</string>
|
<string name="demo_mode">Demo mode</string>
|
||||||
|
|
||||||
<!-- Activity title for deletion helper. [CHAR LIMIT=25] -->
|
|
||||||
<string name="deletion_helper_title">Remove from Device</string>
|
|
||||||
<!-- Summary of how much storage an app is using and the number of days since last use. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_app_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> • Last used <xliff:g id="days" example="67">%2$d</xliff:g> days ago</string>
|
|
||||||
<!-- Summary of how much storage an app is using when it has never been used before. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_app_summary_never_used"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> • Never used before</string>
|
|
||||||
<!-- Summary of how much storage an app is using when its last use is unknown. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_app_summary_unknown_used"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> • Not sure when last used</string>
|
|
||||||
<!-- Button which clears out storage in the deletion helper. [CHAR LIMIT=60]-->
|
|
||||||
<string name="deletion_helper_free_button">Free up <xliff:g id="freeable" example="1.2GB">%1$s</xliff:g></string>
|
|
||||||
|
|
||||||
<!-- Title text for connecting to customer support [CHAR LIMIT=80]-->
|
<!-- Title text for connecting to customer support [CHAR LIMIT=80]-->
|
||||||
<string name="support_escalation_title">We\'re here to help</string>
|
<string name="support_escalation_title">We\'re here to help</string>
|
||||||
|
|
||||||
@@ -7610,27 +7599,6 @@
|
|||||||
<!-- Message for telling the user the kind of BT device being displayed in list. -->
|
<!-- Message for telling the user the kind of BT device being displayed in list. -->
|
||||||
<string name="bluetooth_talkback_bluetooth">Bluetooth</string>
|
<string name="bluetooth_talkback_bluetooth">Bluetooth</string>
|
||||||
|
|
||||||
<!-- Preference group title for the photos and videos deletion service. [CHAR LIMIT=40]-->
|
|
||||||
<string name="deletion_helper_photos_title">Photos & Videos (<xliff:g id="num_items">%1$d</xliff:g>)</string>
|
|
||||||
|
|
||||||
<!-- Summary of how much backed up storage that photos and videos service can clear from the local device. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_photos_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> • Older than <xliff:g id="days">%2$d</xliff:g> days</string>
|
|
||||||
|
|
||||||
<!-- Preference title for the downloads deletion service. [CHAR LIMIT=40]-->
|
|
||||||
<string name="deletion_helper_downloads_title">Downloads (<xliff:g id="numItems" example="67">%1$d</xliff:g>)</string>
|
|
||||||
|
|
||||||
<!-- Summary of how much stale data can be cleared from the local download folder. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_downloads_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> • Last modified <xliff:g id="days">%2$s</xliff:g></string>
|
|
||||||
|
|
||||||
<!-- Summary for when when there is nothing in the downloads folder to clear. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_downloads_summary_empty"><xliff:g id="used" example="1.2GB">%1$s</xliff:g></string>
|
|
||||||
|
|
||||||
<!-- Message to warn the user before clearing space in the deletion helper. [CHAR LIMIT=NONE] -->
|
|
||||||
<string name="deletion_helper_clear_dialog_message">Remove <xliff:g id="clearable_bytes" example="1.2GB">%1$s</xliff:g> from your device.</string>
|
|
||||||
|
|
||||||
<!-- Button label for the dialog prompt for clearing data in deletion helper. [CHAR LIMIT=40] -->
|
|
||||||
<string name="deletion_helper_clear_dialog_remove">Remove</string>
|
|
||||||
|
|
||||||
<!-- Used as title on the automatic storage manager settings. [CHAR LIMIT=60] -->
|
<!-- Used as title on the automatic storage manager settings. [CHAR LIMIT=60] -->
|
||||||
<string name="automatic_storage_manager_settings">Storage manager</string>
|
<string name="automatic_storage_manager_settings">Storage manager</string>
|
||||||
|
|
||||||
@@ -7640,21 +7608,6 @@
|
|||||||
<!-- Dropdown preference title for dropdown describing how many days of data to retain.-->
|
<!-- Dropdown preference title for dropdown describing how many days of data to retain.-->
|
||||||
<string name="automatic_storage_manager_days_title">Remove photos & videos</string>
|
<string name="automatic_storage_manager_days_title">Remove photos & videos</string>
|
||||||
|
|
||||||
<!-- Title for the dialog to up sell the storage manager. [CHAR LIMIT=NONE] -->
|
|
||||||
<string name="deletion_helper_upsell_title"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> now free. Manage storage automatically?</string>
|
|
||||||
<!-- Summary for the dialog to up sell the storage manager. [CHAR LIMIT=NONE] -->
|
|
||||||
<string name="deletion_helper_upsell_summary">Let Storage manager automatically free up space by removing backed up content from your device?</string>
|
|
||||||
<!-- Button to delay turning on the storage manager on the storage manager upsell. [CHAR LIMIT=20]-->
|
|
||||||
<string name="deletion_helper_upsell_cancel">No thanks</string>
|
|
||||||
<!-- Button to activate the storage manager on the storage manager upsell. [CHAR LIMIT=20]-->
|
|
||||||
<string name="deletion_helper_upsell_activate">Turn on</string>
|
|
||||||
|
|
||||||
<!-- Title for the apps category in the deletion helper, showing how many apps to delete. [CHAR LIMIT=40]-->
|
|
||||||
<string name="deletion_helper_apps_group_title">Apps (<xliff:g id="num_items">%1$d</xliff:g>)</string>
|
|
||||||
|
|
||||||
<!-- Summary for the apps category in the deletion helper, showing how many space to clear. [CHAR LIMIT=NONE]-->
|
|
||||||
<string name="deletion_helper_apps_group_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> • Last used <xliff:g id="days">%2$d</xliff:g> days ago</string>
|
|
||||||
|
|
||||||
<!-- Category title for the automatic settings in the storage manager settings. [CHAR LIMIT=40] -->
|
<!-- Category title for the automatic settings in the storage manager settings. [CHAR LIMIT=40] -->
|
||||||
<string name="deletion_helper_automatic_title">Automatic</string>
|
<string name="deletion_helper_automatic_title">Automatic</string>
|
||||||
|
|
||||||
@@ -7664,12 +7617,6 @@
|
|||||||
<!-- Preference menu title for accessing the deletion helper from the storage manager settings. [CHAR LIMIT=30]-->
|
<!-- Preference menu title for accessing the deletion helper from the storage manager settings. [CHAR LIMIT=30]-->
|
||||||
<string name="deletion_helper_preference_title">Free space now</string>
|
<string name="deletion_helper_preference_title">Free space now</string>
|
||||||
|
|
||||||
<!-- Preference title for the automatic storage manager toggle. [CHAR LIMIT=60]-->
|
|
||||||
<string name="automatic_storage_manager_preference_title">Storage manager</string>
|
|
||||||
|
|
||||||
<!-- Automatic storage management service label. [CHAR LIMIT=40]-->
|
|
||||||
<string name="automatic_storage_manager_service_label">Automatic Storage Management Service</string>
|
|
||||||
|
|
||||||
<!-- Preference title for gesture settings [CHAR LIMIT=25]-->
|
<!-- Preference title for gesture settings [CHAR LIMIT=25]-->
|
||||||
<string name="gesture_preference_title">Gestures</string>
|
<string name="gesture_preference_title">Gestures</string>
|
||||||
|
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:title="@string/deletion_helper_title">
|
|
||||||
|
|
||||||
<com.android.settings.PhotosDeletionPreference
|
|
||||||
android:key="delete_photos" />
|
|
||||||
|
|
||||||
<com.android.settings.deletionhelper.DownloadsDeletionPreferenceGroup
|
|
||||||
android:key="delete_downloads"
|
|
||||||
android:icon="@drawable/ic_keyboard_arrow_down_black_32"/>
|
|
||||||
|
|
||||||
<com.android.settings.CollapsibleCheckboxPreferenceGroup
|
|
||||||
android:key="apps_group"
|
|
||||||
android:icon="@drawable/ic_keyboard_arrow_down_black_32"/>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
|
@@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.PreferenceGroup;
|
|
||||||
import android.support.v7.preference.PreferenceViewHolder;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Checkable;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CollapsibleCheckboxPreferenceGroup is a preference group that can be expanded or collapsed and
|
|
||||||
* also has a checkbox.
|
|
||||||
*/
|
|
||||||
public class CollapsibleCheckboxPreferenceGroup extends PreferenceGroup implements
|
|
||||||
View.OnClickListener {
|
|
||||||
private boolean mCollapsed;
|
|
||||||
private boolean mChecked;
|
|
||||||
|
|
||||||
public CollapsibleCheckboxPreferenceGroup(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollapsibleCheckboxPreferenceGroup(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setWidgetLayoutResource(com.android.settings.R.layout.preference_widget_checkbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
|
||||||
super.onBindViewHolder(holder);
|
|
||||||
View checkbox = holder.findViewById(com.android.internal.R.id.checkbox);
|
|
||||||
if (checkbox != null && checkbox instanceof Checkable) {
|
|
||||||
((Checkable) checkbox).setChecked(mChecked);
|
|
||||||
checkbox.setClickable(true);
|
|
||||||
checkbox.setFocusable(true);
|
|
||||||
checkbox.setOnClickListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
|
|
||||||
if (titleView != null) {
|
|
||||||
Context context = getContext();
|
|
||||||
TypedValue value = new TypedValue();
|
|
||||||
context.getTheme().resolveAttribute(android.R.attr.colorAccent, value, true);
|
|
||||||
titleView.setTextColor(context.getColor(value.resourceId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addPreference(Preference p) {
|
|
||||||
super.addPreference(p);
|
|
||||||
p.setVisible(!isCollapsed());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The preference click handler.
|
|
||||||
@Override
|
|
||||||
protected void onClick() {
|
|
||||||
super.onClick();
|
|
||||||
setCollapse(!isCollapsed());
|
|
||||||
}
|
|
||||||
|
|
||||||
// The checkbox view click handler.
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
setChecked(!isChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return if the view is collapsed.
|
|
||||||
*/
|
|
||||||
public boolean isCollapsed() {
|
|
||||||
return mCollapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the checked state of the preference.
|
|
||||||
*/
|
|
||||||
public boolean isChecked() {
|
|
||||||
return mChecked;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the checked state and notifies listeners of the state change.
|
|
||||||
*/
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
if (mChecked != checked) {
|
|
||||||
mChecked = checked;
|
|
||||||
|
|
||||||
callChangeListener(checked);
|
|
||||||
notifyDependencyChange(shouldDisableDependents());
|
|
||||||
notifyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCollapse(boolean isCollapsed) {
|
|
||||||
if (mCollapsed == isCollapsed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCollapsed = isCollapsed;
|
|
||||||
if (isCollapsed) {
|
|
||||||
hideDropdownPreferences();
|
|
||||||
} else {
|
|
||||||
showDropdownPreferences();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDropdownPreferences() {
|
|
||||||
setAllPreferencesVisibility(true);
|
|
||||||
setIcon(R.drawable.ic_keyboard_arrow_down_black_32);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hideDropdownPreferences() {
|
|
||||||
setAllPreferencesVisibility(false);
|
|
||||||
setIcon(R.drawable.ic_keyboard_arrow_up_black_32);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAllPreferencesVisibility(boolean visible) {
|
|
||||||
for (int i = 0; i < getPreferenceCount(); i++) {
|
|
||||||
Preference p = getPreference(i);
|
|
||||||
p.setVisible(visible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
|
|
||||||
import android.support.v7.preference.CheckBoxPreference;
|
|
||||||
import android.support.v7.preference.PreferenceViewHolder;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import com.android.settings.deletionhelper.DeletionType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preference to handle the deletion of various data types in the Deletion Helper.
|
|
||||||
*/
|
|
||||||
public abstract class DeletionPreference extends CheckBoxPreference implements
|
|
||||||
DeletionType.FreeableChangedListener, OnPreferenceChangeListener {
|
|
||||||
private DeletionType.FreeableChangedListener mListener;
|
|
||||||
private boolean mChecked;
|
|
||||||
private long mFreeableBytes;
|
|
||||||
private int mFreeableItems;
|
|
||||||
private DeletionType mDeletionService;
|
|
||||||
|
|
||||||
public DeletionPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
|
||||||
super.onBindViewHolder(holder);
|
|
||||||
final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
|
|
||||||
if (titleView != null) {
|
|
||||||
titleView.setTextColor(getTintColor(getContext()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes which can be cleared by the deletion service.
|
|
||||||
* @return The number of bytes.
|
|
||||||
*/
|
|
||||||
public long getFreeableBytes() {
|
|
||||||
return mChecked ? mFreeableBytes : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a listener to be called back on when the freeable bytes have changed.
|
|
||||||
* @param listener The callback listener.
|
|
||||||
*/
|
|
||||||
public void registerFreeableChangedListener(DeletionType.FreeableChangedListener listener) {
|
|
||||||
mListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a deletion service to update the preference's information.
|
|
||||||
* @param deletionService A photo/video deletion service.
|
|
||||||
*/
|
|
||||||
public void registerDeletionService(DeletionType deletionService) {
|
|
||||||
mDeletionService = deletionService;
|
|
||||||
if (mDeletionService != null) {
|
|
||||||
mDeletionService.registerFreeableChangedListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the deletion service powering the preference.
|
|
||||||
* @return The deletion service.
|
|
||||||
*/
|
|
||||||
public DeletionType getDeletionService() {
|
|
||||||
return mDeletionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFreeableChanged(int numItems, long freeableBytes) {
|
|
||||||
mFreeableItems = numItems;
|
|
||||||
mFreeableBytes = freeableBytes;
|
|
||||||
maybeUpdateListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
mChecked = (boolean) newValue;
|
|
||||||
maybeUpdateListener();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getTintColor(Context context) {
|
|
||||||
TypedValue value = new TypedValue();
|
|
||||||
context.getTheme().resolveAttribute(android.R.attr.colorAccent, value, true);
|
|
||||||
return context.getColor(value.resourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeUpdateListener() {
|
|
||||||
if (mListener != null) {
|
|
||||||
mListener.onFreeableChanged(mFreeableItems, getFreeableBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preference to handle the deletion of photos and videos in the Deletion Helper.
|
|
||||||
*/
|
|
||||||
public class PhotosDeletionPreference extends DeletionPreference {
|
|
||||||
public static final int DAYS_TO_KEEP = 30;
|
|
||||||
|
|
||||||
public PhotosDeletionPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setIcon(getIcon(context));
|
|
||||||
updatePreferenceText(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the title and summary of the preference with fresh information.
|
|
||||||
*/
|
|
||||||
public void updatePreferenceText(int items, long bytes) {
|
|
||||||
Context context = getContext();
|
|
||||||
setTitle(context.getString(R.string.deletion_helper_photos_title, items));
|
|
||||||
setSummary(context.getString(R.string.deletion_helper_photos_summary,
|
|
||||||
Formatter.formatFileSize(context, bytes), DAYS_TO_KEEP));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFreeableChanged(int items, long bytes) {
|
|
||||||
super.onFreeableChanged(items, bytes);
|
|
||||||
updatePreferenceText(items, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Drawable getIcon(Context context) {
|
|
||||||
final Drawable iconDrawable;
|
|
||||||
try {
|
|
||||||
Resources resources = context.getResources();
|
|
||||||
final int resId = resources.getIdentifier("ic_photos_black_24", "drawable",
|
|
||||||
context.getPackageName());
|
|
||||||
iconDrawable = context.getDrawable(resId);
|
|
||||||
} catch (Resources.NotFoundException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return iconDrawable;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -74,7 +74,6 @@ import com.android.settings.bluetooth.BluetoothSettings;
|
|||||||
import com.android.settings.dashboard.DashboardContainerFragment;
|
import com.android.settings.dashboard.DashboardContainerFragment;
|
||||||
import com.android.settings.dashboard.SearchResultsSummary;
|
import com.android.settings.dashboard.SearchResultsSummary;
|
||||||
import com.android.settings.datausage.DataUsageSummary;
|
import com.android.settings.datausage.DataUsageSummary;
|
||||||
import com.android.settings.deletionhelper.DeletionHelperFragment;
|
|
||||||
import com.android.settings.deviceinfo.ImeiInformation;
|
import com.android.settings.deviceinfo.ImeiInformation;
|
||||||
import com.android.settings.deviceinfo.PrivateVolumeForget;
|
import com.android.settings.deviceinfo.PrivateVolumeForget;
|
||||||
import com.android.settings.deviceinfo.PrivateVolumeSettings;
|
import com.android.settings.deviceinfo.PrivateVolumeSettings;
|
||||||
@@ -343,7 +342,6 @@ public class SettingsActivity extends SettingsDrawerActivity
|
|||||||
WallpaperTypeSettings.class.getName(),
|
WallpaperTypeSettings.class.getName(),
|
||||||
VrListenerSettings.class.getName(),
|
VrListenerSettings.class.getName(),
|
||||||
ManagedProfileSettings.class.getName(),
|
ManagedProfileSettings.class.getName(),
|
||||||
DeletionHelperFragment.class.getName(),
|
|
||||||
ChooseAccountActivity.class.getName(),
|
ChooseAccountActivity.class.getName(),
|
||||||
IccLockSettings.class.getName(),
|
IccLockSettings.class.getName(),
|
||||||
ImeiInformation.class.getName(),
|
ImeiInformation.class.getName(),
|
||||||
|
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
|
||||||
* except in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the
|
|
||||||
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the specific language governing
|
|
||||||
* permissions and limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v14.preference.SwitchPreference;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.PreferenceViewHolder;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Switch;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import com.android.settings.deletionhelper.AppStateUsageStatsBridge.UsageStatsState;
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preference item for an app with a switch to signify if it should be uninstalled.
|
|
||||||
* This shows the name and icon of the app along with the days since its last use.
|
|
||||||
*/
|
|
||||||
public class AppDeletionPreference extends SwitchPreference {
|
|
||||||
private AppEntry mEntry;
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
public AppDeletionPreference(Context context, AppEntry item, ApplicationsState state) {
|
|
||||||
super(context);
|
|
||||||
mEntry = item;
|
|
||||||
mContext = context;
|
|
||||||
setLayoutResource(com.android.settings.R.layout.preference_app);
|
|
||||||
setWidgetLayoutResource(R.layout.widget_text_views);
|
|
||||||
|
|
||||||
synchronized (item) {
|
|
||||||
state.ensureIcon(item);
|
|
||||||
if (item.icon != null)
|
|
||||||
setIcon(item.icon);
|
|
||||||
if (item.label != null)
|
|
||||||
setTitle(item.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
|
||||||
super.onBindViewHolder(holder);
|
|
||||||
Switch switchWidget = (Switch) holder.findViewById(com.android.internal.R.id.switch_widget);
|
|
||||||
switchWidget.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
TextView summary = (TextView) holder.findViewById(R.id.widget_text1);
|
|
||||||
updateSummaryText(summary);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPackageName() {
|
|
||||||
return mEntry.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSummaryText(TextView summary) {
|
|
||||||
if (mEntry.extraInfo == null) return;
|
|
||||||
if (mEntry.size == ApplicationsState.SIZE_UNKNOWN ||
|
|
||||||
mEntry.size == ApplicationsState.SIZE_INVALID) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UsageStatsState extraData = (UsageStatsState) mEntry.extraInfo;
|
|
||||||
String fileSize = Formatter.formatFileSize(mContext, mEntry.size);
|
|
||||||
if (extraData.daysSinceLastUse == AppStateUsageStatsBridge.NEVER_USED) {
|
|
||||||
summary.setText(mContext.getString(R.string.deletion_helper_app_summary_never_used,
|
|
||||||
fileSize));
|
|
||||||
} else if (extraData.daysSinceLastUse == AppStateUsageStatsBridge.UNKNOWN_LAST_USE) {
|
|
||||||
summary.setText(mContext.getString(R.string.deletion_helper_app_summary_unknown_used,
|
|
||||||
fileSize));
|
|
||||||
} else {
|
|
||||||
summary.setText(mContext.getString(R.string.deletion_helper_app_summary,
|
|
||||||
fileSize,
|
|
||||||
extraData.daysSinceLastUse));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.usage.UsageStats;
|
|
||||||
import android.app.usage.UsageStatsManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.android.settings.applications.AppStateBaseBridge;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects data from the UsageStatsManager to the ApplicationsState.
|
|
||||||
*/
|
|
||||||
public class AppStateUsageStatsBridge extends AppStateBaseBridge {
|
|
||||||
private static final String TAG = "AppStateUsageStatsBridge";
|
|
||||||
private UsageStatsManager mUsageStatsManager;
|
|
||||||
private PackageManager mPm;
|
|
||||||
public static final long NEVER_USED = -1;
|
|
||||||
public static final long UNKNOWN_LAST_USE = -2;
|
|
||||||
public static final long UNUSED_DAYS_DELETION_THRESHOLD = 60;
|
|
||||||
|
|
||||||
public AppStateUsageStatsBridge(Context context, ApplicationsState appState,
|
|
||||||
Callback callback) {
|
|
||||||
super(appState, callback);
|
|
||||||
mUsageStatsManager =
|
|
||||||
(UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
|
|
||||||
mPm = context.getPackageManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void loadAllExtraInfo() {
|
|
||||||
ArrayList<AppEntry> apps = mAppSession.getAllApps();
|
|
||||||
if (apps == null) return;
|
|
||||||
|
|
||||||
final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats(0,
|
|
||||||
System.currentTimeMillis());
|
|
||||||
for (AppEntry entry : apps) {
|
|
||||||
UsageStats usageStats = map.get(entry.info.packageName);
|
|
||||||
entry.extraInfo = new UsageStatsState(getDaysSinceLastUse(usageStats),
|
|
||||||
getDaysSinceInstalled(entry.info.packageName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
|
|
||||||
Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats(0,
|
|
||||||
System.currentTimeMillis());
|
|
||||||
UsageStats usageStats = map.get(app.info.packageName);
|
|
||||||
app.extraInfo = new UsageStatsState(getDaysSinceLastUse(usageStats),
|
|
||||||
getDaysSinceInstalled(app.info.packageName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getDaysSinceLastUse(UsageStats stats) {
|
|
||||||
if (stats == null) {
|
|
||||||
return NEVER_USED;
|
|
||||||
}
|
|
||||||
long lastUsed = stats.getLastTimeUsed();
|
|
||||||
// Sometimes, a usage is recorded without a time and we don't know when the use was.
|
|
||||||
if (lastUsed == 0) {
|
|
||||||
return UNKNOWN_LAST_USE;
|
|
||||||
}
|
|
||||||
return TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - lastUsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getDaysSinceInstalled(String packageName) {
|
|
||||||
PackageInfo pi = null;
|
|
||||||
try {
|
|
||||||
pi = mPm.getPackageInfo(packageName, 0);
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
Log.e(TAG, packageName + " was not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pi == null) {
|
|
||||||
return NEVER_USED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - pi.firstInstallTime));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters only non-system apps which haven't been used in the last 60 days. If an app's last
|
|
||||||
* usage is unknown, it is skipped.
|
|
||||||
*/
|
|
||||||
public static final AppFilter FILTER_USAGE_STATS = new AppFilter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean filterApp(AppEntry info) {
|
|
||||||
if (info == null) return false;
|
|
||||||
return isExtraInfoValid(info.extraInfo) && !isBundled(info)
|
|
||||||
&& !isPersistentProcess(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isExtraInfoValid(Object extraInfo) {
|
|
||||||
if (extraInfo == null || !(extraInfo instanceof UsageStatsState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
UsageStatsState state = (UsageStatsState) extraInfo;
|
|
||||||
long mostRecentUse = Math.max(state.daysSinceFirstInstall, state.daysSinceLastUse);
|
|
||||||
return mostRecentUse >= UNUSED_DAYS_DELETION_THRESHOLD || mostRecentUse == NEVER_USED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBundled(AppEntry info) {
|
|
||||||
return (info.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPersistentProcess(AppEntry info) {
|
|
||||||
return (info.info.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UsageStatsState contains the days since the last use and first install of a given app.
|
|
||||||
*/
|
|
||||||
public static class UsageStatsState {
|
|
||||||
public long daysSinceLastUse;
|
|
||||||
public long daysSinceFirstInstall;
|
|
||||||
|
|
||||||
public UsageStatsState(long daysSinceLastUse, long daysSinceFirstInstall) {
|
|
||||||
this.daysSinceLastUse = daysSinceLastUse;
|
|
||||||
this.daysSinceFirstInstall = daysSinceFirstInstall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.job.JobInfo;
|
|
||||||
import android.app.job.JobScheduler;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link BroadcastReceiver} listening for {@link Intent#ACTION_BOOT_COMPLETED} broadcasts to
|
|
||||||
* schedule an automatic storage management job. Automatic storage management jobs are only
|
|
||||||
* scheduled once a day for a plugged in device.
|
|
||||||
*/
|
|
||||||
public class AutomaticStorageBroadcastReceiver extends BroadcastReceiver {
|
|
||||||
private static final int AUTOMATIC_STORAGE_JOB_ID = 0;
|
|
||||||
private static final long PERIOD = DateUtils.DAY_IN_MILLIS;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
JobScheduler jobScheduler =
|
|
||||||
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
|
||||||
ComponentName component = new ComponentName(context,
|
|
||||||
AutomaticStorageManagementJobService.class);
|
|
||||||
JobInfo job = new JobInfo.Builder(AUTOMATIC_STORAGE_JOB_ID, component)
|
|
||||||
.setRequiresCharging(true)
|
|
||||||
.setRequiresDeviceIdle(true)
|
|
||||||
.setPeriodic(PERIOD)
|
|
||||||
.build();
|
|
||||||
jobScheduler.schedule(job);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.job.JobParameters;
|
|
||||||
import android.app.job.JobService;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.storage.StorageManager;
|
|
||||||
import android.os.storage.VolumeInfo;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settings.overlay.StorageManagementJobProvider;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link JobService} class to start automatic storage clearing jobs to free up space. The job only
|
|
||||||
* starts if the device is under a certain percent of free storage.
|
|
||||||
*/
|
|
||||||
public class AutomaticStorageManagementJobService extends JobService {
|
|
||||||
private static final String TAG = "AsmJobService";
|
|
||||||
private static final String SHARED_PREFRENCES_NAME = "automatic_storage_manager_settings";
|
|
||||||
private static final String KEY_DAYS_TO_RETAIN = "days_to_retain";
|
|
||||||
|
|
||||||
private static final long DEFAULT_LOW_FREE_PERCENT = 15;
|
|
||||||
|
|
||||||
private StorageManagementJobProvider mProvider;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onStartJob(JobParameters args) {
|
|
||||||
boolean isEnabled =
|
|
||||||
Settings.Secure.getInt(getContentResolver(),
|
|
||||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0;
|
|
||||||
if (!isEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageManager manager = getSystemService(StorageManager.class);
|
|
||||||
VolumeInfo internalVolume = manager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
|
|
||||||
|
|
||||||
final File dataPath = internalVolume.getPath();
|
|
||||||
if (!volumeNeedsManagement(dataPath)) {
|
|
||||||
Log.i(TAG, "Skipping automatic storage management.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mProvider = FeatureFactory.getFactory(this).getStorageManagementJobProvider();
|
|
||||||
if (mProvider != null) {
|
|
||||||
return mProvider.onStartJob(this, args, getDaysToRetain());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onStopJob(JobParameters args) {
|
|
||||||
if (mProvider != null) {
|
|
||||||
return mProvider.onStopJob(this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getDaysToRetain() {
|
|
||||||
SharedPreferences sharedPreferences =
|
|
||||||
getSharedPreferences(SHARED_PREFRENCES_NAME, Context.MODE_PRIVATE);
|
|
||||||
return sharedPreferences.getInt(KEY_DAYS_TO_RETAIN,
|
|
||||||
AutomaticStorageManagerSettings.DEFAULT_DAYS_TO_RETAIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean volumeNeedsManagement(final File dataPath) {
|
|
||||||
long lowStorageThreshold = (dataPath.getTotalSpace() * DEFAULT_LOW_FREE_PERCENT) / 100;
|
|
||||||
return dataPath.getFreeSpace() < lowStorageThreshold;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -18,6 +18,7 @@ package com.android.settings.deletionhelper;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
@@ -115,8 +116,8 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
if (KEY_DELETION_HELPER.equals(preference.getKey())) {
|
if (KEY_DELETION_HELPER.equals(preference.getKey())) {
|
||||||
startFragment(this, DeletionHelperFragment.class.getCanonicalName(),
|
Intent intent = new Intent(Settings.ACTION_DELETION_HELPER_SETTINGS);
|
||||||
R.string.deletion_helper_title, 0, null);
|
getContext().startActivity(intent);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment used to confirm that the user wishes to delete a certain amount of data.
|
|
||||||
*/
|
|
||||||
public class ConfirmDeletionDialog extends DialogFragment implements
|
|
||||||
DialogInterface.OnClickListener {
|
|
||||||
public static final String TAG = "ConfirmDeletionDialog";
|
|
||||||
private static final String ARG_TOTAL_SPACE = "total_freeable";
|
|
||||||
|
|
||||||
public static ConfirmDeletionDialog newInstance(long freeableBytes) {
|
|
||||||
Bundle args = new Bundle(1);
|
|
||||||
args.putLong(ARG_TOTAL_SPACE, freeableBytes);
|
|
||||||
|
|
||||||
ConfirmDeletionDialog dialog = new ConfirmDeletionDialog();
|
|
||||||
dialog.setArguments(args);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final Bundle args = getArguments();
|
|
||||||
long totalFreeableSpace = args.getLong(ARG_TOTAL_SPACE);
|
|
||||||
|
|
||||||
final Context context = getContext();
|
|
||||||
return new AlertDialog.Builder(context)
|
|
||||||
.setMessage(context.getString(R.string.deletion_helper_clear_dialog_message,
|
|
||||||
Formatter.formatFileSize(context, totalFreeableSpace)))
|
|
||||||
.setPositiveButton(R.string.deletion_helper_clear_dialog_remove, this)
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
((DeletionHelperFragment) getTargetFragment()).clearData();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,415 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import com.android.settings.CollapsibleCheckboxPreferenceGroup;
|
|
||||||
import com.android.settings.PhotosDeletionPreference;
|
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.internal.logging.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.applications.AppStateBaseBridge;
|
|
||||||
import com.android.settings.overlay.DeletionHelperFeatureProvider;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState.Callbacks;
|
|
||||||
import com.android.settingslib.applications.ApplicationsState.Session;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings screen for the deletion helper, which manually removes data which is not recently used.
|
|
||||||
*/
|
|
||||||
public class DeletionHelperFragment extends SettingsPreferenceFragment implements
|
|
||||||
ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
|
|
||||||
Preference.OnPreferenceChangeListener, DeletionType.FreeableChangedListener,
|
|
||||||
View.OnClickListener {
|
|
||||||
public static final int CLEAR_DATA_RESULT = 1;
|
|
||||||
public static final String FREED_BYTES_KEY = "freed";
|
|
||||||
|
|
||||||
private static final String TAG = "DeletionHelperFragment";
|
|
||||||
|
|
||||||
private static final String EXTRA_HAS_BRIDGE = "hasBridge";
|
|
||||||
private static final String EXTRA_HAS_SIZES = "hasSizes";
|
|
||||||
private static final String EXTRA_CHECKED_SET = "checkedSet";
|
|
||||||
|
|
||||||
private static final String KEY_APPS_GROUP = "apps_group";
|
|
||||||
private static final String KEY_PHOTOS_VIDEOS_PREFERENCE = "delete_photos";
|
|
||||||
private static final String KEY_DOWNLOADS_PREFERENCE = "delete_downloads";
|
|
||||||
|
|
||||||
private static final int DOWNLOADS_LOADER_ID = 1;
|
|
||||||
|
|
||||||
private Button mCancel, mFree;
|
|
||||||
private CollapsibleCheckboxPreferenceGroup mApps;
|
|
||||||
private PhotosDeletionPreference mPhotoPreference;
|
|
||||||
private DownloadsDeletionPreferenceGroup mDownloadsPreference;
|
|
||||||
|
|
||||||
private ApplicationsState mState;
|
|
||||||
private Session mSession;
|
|
||||||
private HashSet<String> mCheckedApplications;
|
|
||||||
private AppStateUsageStatsBridge mDataUsageBridge;
|
|
||||||
private ArrayList<AppEntry> mAppEntries;
|
|
||||||
private boolean mHasReceivedAppEntries, mHasReceivedBridgeCallback, mFinishedLoading;
|
|
||||||
private DeletionHelperFeatureProvider mProvider;
|
|
||||||
private DeletionType mPhotoVideoDeletion;
|
|
||||||
private DownloadsDeletionType mDownloadsDeletion;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setAnimationAllowed(true);
|
|
||||||
Application app = getActivity().getApplication();
|
|
||||||
mState = ApplicationsState.getInstance(app);
|
|
||||||
mSession = mState.newSession(this);
|
|
||||||
mCheckedApplications = new HashSet<>();
|
|
||||||
mDataUsageBridge = new AppStateUsageStatsBridge(getActivity(), mState, this);
|
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.deletion_helper_list);
|
|
||||||
mApps = (CollapsibleCheckboxPreferenceGroup) findPreference(KEY_APPS_GROUP);
|
|
||||||
mPhotoPreference = (PhotosDeletionPreference) findPreference(KEY_PHOTOS_VIDEOS_PREFERENCE);
|
|
||||||
mDownloadsPreference =
|
|
||||||
(DownloadsDeletionPreferenceGroup) findPreference(KEY_DOWNLOADS_PREFERENCE);
|
|
||||||
mProvider =
|
|
||||||
FeatureFactory.getFactory(app).getDeletionHelperFeatureProvider();
|
|
||||||
if (mProvider != null) {
|
|
||||||
mPhotoVideoDeletion = mProvider.createPhotoVideoDeletionType(getContext());
|
|
||||||
}
|
|
||||||
mDownloadsDeletion = new DownloadsDeletionType(getActivity());
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mHasReceivedAppEntries =
|
|
||||||
savedInstanceState.getBoolean(EXTRA_HAS_SIZES, false);
|
|
||||||
mHasReceivedBridgeCallback =
|
|
||||||
savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
|
|
||||||
mCheckedApplications =
|
|
||||||
(HashSet<String>) savedInstanceState.getSerializable(EXTRA_CHECKED_SET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeButtons(View v) {
|
|
||||||
mCancel = (Button) v.findViewById(R.id.skip_button);
|
|
||||||
mCancel.setText(R.string.cancel);
|
|
||||||
mCancel.setOnClickListener(this);
|
|
||||||
mCancel.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
mFree = (Button) v.findViewById(R.id.next_button);
|
|
||||||
mFree.setText(R.string.storage_menu_free);
|
|
||||||
mFree.setOnClickListener(this);
|
|
||||||
|
|
||||||
Button back = (Button) v.findViewById(R.id.back_button);
|
|
||||||
back.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeDeletionPreferences() {
|
|
||||||
if (mProvider == null) {
|
|
||||||
getPreferenceScreen().removePreference(mPhotoPreference);
|
|
||||||
mPhotoPreference = null;
|
|
||||||
} else {
|
|
||||||
mPhotoPreference.registerFreeableChangedListener(this);
|
|
||||||
mPhotoPreference.registerDeletionService(mPhotoVideoDeletion);
|
|
||||||
}
|
|
||||||
|
|
||||||
mDownloadsPreference.registerFreeableChangedListener(this);
|
|
||||||
mDownloadsPreference.registerDeletionService(mDownloadsDeletion);
|
|
||||||
mApps.setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View v, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(v, savedInstanceState);
|
|
||||||
initializeButtons(v);
|
|
||||||
initializeDeletionPreferences();
|
|
||||||
setLoading(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
mSession.resume();
|
|
||||||
mDataUsageBridge.resume();
|
|
||||||
mDownloadsDeletion.onResume();
|
|
||||||
getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(), mDownloadsDeletion);
|
|
||||||
|
|
||||||
if (mPhotoVideoDeletion != null) {
|
|
||||||
mPhotoVideoDeletion.onResume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putBoolean(EXTRA_HAS_SIZES, mHasReceivedAppEntries);
|
|
||||||
outState.putBoolean(EXTRA_HAS_BRIDGE, mHasReceivedBridgeCallback);
|
|
||||||
outState.putSerializable(EXTRA_CHECKED_SET, mCheckedApplications);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
mDataUsageBridge.pause();
|
|
||||||
mSession.pause();
|
|
||||||
mDownloadsDeletion.onPause();
|
|
||||||
|
|
||||||
if (mPhotoVideoDeletion != null) {
|
|
||||||
mPhotoVideoDeletion.onPause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rebuild() {
|
|
||||||
// Only rebuild if we have the packages and their usage stats.
|
|
||||||
if (!mHasReceivedBridgeCallback || !mHasReceivedAppEntries) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ArrayList<AppEntry> apps =
|
|
||||||
mSession.rebuild(AppStateUsageStatsBridge.FILTER_USAGE_STATS,
|
|
||||||
ApplicationsState.SIZE_COMPARATOR);
|
|
||||||
if (apps == null) return;
|
|
||||||
mAppEntries = apps;
|
|
||||||
refreshAppGroup(apps);
|
|
||||||
|
|
||||||
// All applications should be filled in if we've received the sizes.
|
|
||||||
// setLoading being called multiple times causes flickering, so we only do it once.
|
|
||||||
if (mHasReceivedAppEntries && !mFinishedLoading) {
|
|
||||||
mFinishedLoading = true;
|
|
||||||
setLoading(false, true);
|
|
||||||
getButtonBar().setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
updateFreeButtonText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateFreeButtonText() {
|
|
||||||
mFree.setText(String.format(getActivity().getString(R.string.deletion_helper_free_button),
|
|
||||||
Formatter.formatFileSize(getActivity(), getTotalFreeableSpace())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRunningStateChanged(boolean running) {
|
|
||||||
// No-op.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPackageListChanged() {
|
|
||||||
rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRebuildComplete(ArrayList<AppEntry> apps) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPackageIconChanged() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPackageSizeChanged(String packageName) {
|
|
||||||
rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAllSizesComputed() {
|
|
||||||
rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLauncherInfoChanged() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadEntriesCompleted() {
|
|
||||||
mHasReceivedAppEntries = true;
|
|
||||||
rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExtraInfoUpdated() {
|
|
||||||
mHasReceivedBridgeCallback = true;
|
|
||||||
rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getMetricsCategory() {
|
|
||||||
return MetricsEvent.DEVICEINFO_STORAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
boolean checked = (boolean) newValue;
|
|
||||||
if (preference.getKey().equals(mApps.getKey())) {
|
|
||||||
return toggleAllApps(checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
String packageName = ((AppDeletionPreference) preference).getPackageName();
|
|
||||||
if (checked) {
|
|
||||||
mCheckedApplications.add(packageName);
|
|
||||||
} else {
|
|
||||||
mCheckedApplications.remove(packageName);
|
|
||||||
|
|
||||||
// We remove the preference change listener to avoid toggling every app on and off.
|
|
||||||
mApps.setOnPreferenceChangeListener(null);
|
|
||||||
mApps.setChecked(false);
|
|
||||||
mApps.setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
updateFreeButtonText();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFreeableChanged(int numItems, long freeableBytes) {
|
|
||||||
updateFreeButtonText();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (v.getId() == mFree.getId()) {
|
|
||||||
ConfirmDeletionDialog dialog =
|
|
||||||
ConfirmDeletionDialog.newInstance(getTotalFreeableSpace());
|
|
||||||
// The 0 is a placeholder for an optional result code.
|
|
||||||
dialog.setTargetFragment(this, 0);
|
|
||||||
dialog.show(getFragmentManager(), ConfirmDeletionDialog.TAG);
|
|
||||||
} else {
|
|
||||||
finishFragment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears out the selected apps and data from the device and closes the fragment.
|
|
||||||
*/
|
|
||||||
protected void clearData() {
|
|
||||||
// This should be fine as long as there is only one extra deletion feature.
|
|
||||||
// In the future, this should be done in an async queue in order to not
|
|
||||||
// interfere with the simultaneous PackageDeletionTask.
|
|
||||||
if (mPhotoPreference != null && mPhotoPreference.isChecked()) {
|
|
||||||
mPhotoVideoDeletion.clearFreeableData();
|
|
||||||
}
|
|
||||||
mDownloadsDeletion.clearFreeableData();
|
|
||||||
|
|
||||||
ArraySet<String> apps = new ArraySet<>();
|
|
||||||
for (AppEntry entry : mAppEntries) {
|
|
||||||
if (mCheckedApplications.contains(entry.label)) {
|
|
||||||
synchronized (entry) {
|
|
||||||
apps.add(entry.info.packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: If needed, add an action on the callback.
|
|
||||||
PackageDeletionTask task = new PackageDeletionTask(getActivity().getPackageManager(), apps,
|
|
||||||
new PackageDeletionTask.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError() {
|
|
||||||
Log.e(TAG, "An error occurred while uninstalling packages.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Intent data = new Intent();
|
|
||||||
data.putExtra(FREED_BYTES_KEY, getTotalFreeableSpace());
|
|
||||||
getActivity().setResult(CLEAR_DATA_RESULT, data);
|
|
||||||
|
|
||||||
task.run();
|
|
||||||
finishFragment();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getTotalFreeableSpace() {
|
|
||||||
long freeableSpace = 0;
|
|
||||||
freeableSpace += getTotalAppsFreeableSpace(false);
|
|
||||||
if (mPhotoPreference != null) {
|
|
||||||
freeableSpace += mPhotoPreference.getFreeableBytes();
|
|
||||||
}
|
|
||||||
freeableSpace += mDownloadsDeletion.getFreeableBytes();
|
|
||||||
return freeableSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshAppGroup(ArrayList<AppEntry> apps) {
|
|
||||||
int entryCount = apps.size();
|
|
||||||
cacheRemoveAllPrefs(mApps);
|
|
||||||
for (int i = 0; i < entryCount; i++) {
|
|
||||||
AppEntry entry = apps.get(i);
|
|
||||||
final String packageName = entry.label;
|
|
||||||
AppDeletionPreference preference =
|
|
||||||
(AppDeletionPreference) getCachedPreference(entry.label);
|
|
||||||
if (preference == null) {
|
|
||||||
preference = new AppDeletionPreference(getActivity(), entry, mState);
|
|
||||||
preference.setKey(packageName);
|
|
||||||
preference.setOnPreferenceChangeListener(this);
|
|
||||||
mApps.addPreference(preference);
|
|
||||||
}
|
|
||||||
preference.setChecked(mCheckedApplications.contains(packageName));
|
|
||||||
preference.setOrder(i);
|
|
||||||
}
|
|
||||||
removeCachedPrefs(mApps);
|
|
||||||
updateAppsGroupText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getTotalAppsFreeableSpace(boolean countUnchecked) {
|
|
||||||
long freeableSpace = 0;
|
|
||||||
if (mAppEntries != null) {
|
|
||||||
for (int i = 0; i < mAppEntries.size(); i++) {
|
|
||||||
final AppEntry entry = mAppEntries.get(i);
|
|
||||||
long entrySize = mAppEntries.get(i).size;
|
|
||||||
// If the entrySize is negative, it is either an unknown size or an error occurred.
|
|
||||||
if ((countUnchecked ||
|
|
||||||
mCheckedApplications.contains(entry.label)) && entrySize > 0) {
|
|
||||||
freeableSpace += entrySize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return freeableSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateAppsGroupText() {
|
|
||||||
if (mAppEntries != null) {
|
|
||||||
Activity app = getActivity();
|
|
||||||
mApps.setTitle(app.getString(R.string.deletion_helper_apps_group_title,
|
|
||||||
mAppEntries.size()));
|
|
||||||
mApps.setSummary(app.getString(R.string.deletion_helper_apps_group_summary,
|
|
||||||
Formatter.formatFileSize(app,
|
|
||||||
getTotalAppsFreeableSpace(true)),
|
|
||||||
AppStateUsageStatsBridge.UNUSED_DAYS_DELETION_THRESHOLD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean toggleAllApps(boolean checked) {
|
|
||||||
for (AppEntry entry : mAppEntries) {
|
|
||||||
final String packageName = entry.label;
|
|
||||||
if (checked) {
|
|
||||||
mCheckedApplications.add(packageName);
|
|
||||||
} else {
|
|
||||||
mCheckedApplications.remove(packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refreshAppGroup(mAppEntries);
|
|
||||||
updateFreeButtonText();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v7.preference.PreferenceGroup;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper for the Deletion Helper which can query, clear out, and visualize deletable data.
|
|
||||||
* This could represent a helper for deleting photos, downloads, movies, etc.
|
|
||||||
*/
|
|
||||||
public interface DeletionType {
|
|
||||||
/**
|
|
||||||
* Registers a callback to call when the amount of freeable space is updated.
|
|
||||||
* @param listener A callback.
|
|
||||||
*/
|
|
||||||
void registerFreeableChangedListener(FreeableChangedListener listener);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resumes an operation, intended to be called when the deletion fragment resumes.
|
|
||||||
*/
|
|
||||||
void onResume();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the feature's operations, intended to be called when the deletion fragment is paused.
|
|
||||||
*/
|
|
||||||
void onPause();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously free up the freeable information for the feature.
|
|
||||||
*/
|
|
||||||
void clearFreeableData();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback interface to listen for when a deletion feature's amount of freeable space updates.
|
|
||||||
*/
|
|
||||||
interface FreeableChangedListener {
|
|
||||||
void onFreeableChanged(int numItems, long bytesFreeable);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,157 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import com.android.settings.CollapsibleCheckboxPreferenceGroup;
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DownloadsDeletionPreferenceGroup defines a checkable preference group which contains
|
|
||||||
* downloads file deletion preferences.
|
|
||||||
*/
|
|
||||||
public class DownloadsDeletionPreferenceGroup extends CollapsibleCheckboxPreferenceGroup
|
|
||||||
implements DeletionType.FreeableChangedListener, Preference.OnPreferenceChangeListener {
|
|
||||||
private DownloadsDeletionType mDeletionType;
|
|
||||||
private DeletionType.FreeableChangedListener mListener;
|
|
||||||
|
|
||||||
public DownloadsDeletionPreferenceGroup(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownloadsDeletionPreferenceGroup(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setOrderingAsAdded(false);
|
|
||||||
setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up a deletion type to get info for the preference group.
|
|
||||||
* @param type A {@link DownloadsDeletionType}.
|
|
||||||
*/
|
|
||||||
public void registerDeletionService(DownloadsDeletionType type) {
|
|
||||||
mDeletionType = type;
|
|
||||||
mDeletionType.registerFreeableChangedListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a callback to be called when the amount of freeable space updates.
|
|
||||||
* @param listener The callback listener.
|
|
||||||
*/
|
|
||||||
public void registerFreeableChangedListener(DeletionType.FreeableChangedListener listener) {
|
|
||||||
mListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFreeableChanged(int numItems, long freeableBytes) {
|
|
||||||
updatePreferenceText(numItems, freeableBytes, mDeletionType.getMostRecentLastModified());
|
|
||||||
maybeUpdateListener(numItems, freeableBytes);
|
|
||||||
updateFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
boolean checked = (boolean) newValue;
|
|
||||||
if (!checked) {
|
|
||||||
// Temporarily stop listening to avoid propagating the checked change to children.
|
|
||||||
setOnPreferenceChangeListener(null);
|
|
||||||
setChecked(false);
|
|
||||||
setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the group checkbox changed, we need to toggle every child preference.
|
|
||||||
if (preference == this) {
|
|
||||||
for (int i = 0; i < getPreferenceCount(); i++) {
|
|
||||||
DownloadsFilePreference p = (DownloadsFilePreference) getPreference(i);
|
|
||||||
p.setOnPreferenceChangeListener(null);
|
|
||||||
mDeletionType.toggleFile(p.getFile(), checked);
|
|
||||||
p.setChecked(checked);
|
|
||||||
p.setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
maybeUpdateListener(mDeletionType.getFiles().size(), mDeletionType.getFreeableBytes());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a single DownloadFilePreference changed, we need to toggle just itself.
|
|
||||||
DownloadsFilePreference p = (DownloadsFilePreference) preference;
|
|
||||||
mDeletionType.toggleFile(p.getFile(), checked);
|
|
||||||
maybeUpdateListener(mDeletionType.getFiles().size(), mDeletionType.getFreeableBytes());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void updatePreferenceText(int itemCount, long bytes, long mostRecent) {
|
|
||||||
Context context = getContext();
|
|
||||||
setTitle(context.getString(R.string.deletion_helper_downloads_title, itemCount));
|
|
||||||
// If there are no files to clear, show the empty text instead.
|
|
||||||
if (itemCount != 0) {
|
|
||||||
setSummary(context.getString(R.string.deletion_helper_downloads_summary,
|
|
||||||
Formatter.formatFileSize(context, bytes),
|
|
||||||
DateUtils.getRelativeTimeSpanString(mostRecent,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
DateUtils.DAY_IN_MILLIS,
|
|
||||||
DateUtils.FORMAT_ABBREV_RELATIVE)));
|
|
||||||
} else {
|
|
||||||
setSummary(context.getString(R.string.deletion_helper_downloads_summary_empty,
|
|
||||||
Formatter.formatFileSize(context, bytes)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeUpdateListener(int numItems, long bytesFreeable) {
|
|
||||||
if (mListener != null) {
|
|
||||||
mListener.onFreeableChanged(numItems, bytesFreeable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateFiles() {
|
|
||||||
// TODO: Remove impl overlap with the cached preferences methods in
|
|
||||||
// SettingsPreferenceFragment.
|
|
||||||
|
|
||||||
// Cache the existing file preferences.
|
|
||||||
ArrayMap<String, Preference> cachedPreferences = new ArrayMap<>();
|
|
||||||
for (int i = 0; i < getPreferenceCount(); i++) {
|
|
||||||
Preference p = getPreference(i);
|
|
||||||
cachedPreferences.put(p.getKey(), p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over all of the files and re-use the old file preference, if it exists.
|
|
||||||
Set<File> files = mDeletionType.getFiles();
|
|
||||||
for (File file : files) {
|
|
||||||
DownloadsFilePreference filePreference =
|
|
||||||
(DownloadsFilePreference) cachedPreferences.remove(file.getPath());
|
|
||||||
if (filePreference == null) {
|
|
||||||
filePreference = new DownloadsFilePreference(getContext(), file);
|
|
||||||
filePreference.setChecked(isChecked());
|
|
||||||
filePreference.setOnPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
addPreference(filePreference);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all of the unused preferences.
|
|
||||||
for (Preference p : cachedPreferences.values()) {
|
|
||||||
removePreference(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.LoaderManager.LoaderCallbacks;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Loader;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.ArraySet;
|
|
||||||
import com.android.settings.deletionhelper.FetchDownloadsLoader.DownloadsResult;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The DownloadsDeletionType provides stale download file information to the
|
|
||||||
* {@link DownloadsDeletionPreferenceGroup}.
|
|
||||||
*/
|
|
||||||
public class DownloadsDeletionType implements DeletionType, LoaderCallbacks<DownloadsResult> {
|
|
||||||
private long mBytes;
|
|
||||||
private long mMostRecent;
|
|
||||||
private FreeableChangedListener mListener;
|
|
||||||
private Context mContext;
|
|
||||||
private ArrayMap<File, Boolean> mFiles;
|
|
||||||
|
|
||||||
public DownloadsDeletionType(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
mFiles = new ArrayMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerFreeableChangedListener(FreeableChangedListener listener) {
|
|
||||||
mListener = listener;
|
|
||||||
if (mFiles != null) {
|
|
||||||
maybeUpdateListener();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearFreeableData() {
|
|
||||||
if (mFiles != null) {
|
|
||||||
AsyncTask.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (Map.Entry<File, Boolean> entry : mFiles.entrySet()) {
|
|
||||||
if (entry.getValue()) {
|
|
||||||
entry.getKey().delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Loader<DownloadsResult> onCreateLoader(int id, Bundle args) {
|
|
||||||
return new FetchDownloadsLoader(mContext,
|
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<DownloadsResult> loader, DownloadsResult data) {
|
|
||||||
mMostRecent = data.youngestLastModified;
|
|
||||||
for (File file : data.files) {
|
|
||||||
if (mFiles.containsKey(file)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mFiles.put(file, false);
|
|
||||||
}
|
|
||||||
mBytes = data.totalSize;
|
|
||||||
maybeUpdateListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<DownloadsResult> loader) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the most recent last modified time for any clearable file.
|
|
||||||
* @return The last modified time.
|
|
||||||
*/
|
|
||||||
public long getMostRecentLastModified() {
|
|
||||||
return mMostRecent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the files in the Downloads folder after the loader task finishes.
|
|
||||||
*/
|
|
||||||
public Set<File> getFiles() {
|
|
||||||
if (mFiles == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return mFiles.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle if a file should be deleted when the service is asked to clear files.
|
|
||||||
*/
|
|
||||||
public void toggleFile(File file, boolean checked) {
|
|
||||||
mFiles.put(file, checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes that would be cleared if the deletion tasks runs.
|
|
||||||
*/
|
|
||||||
public long getFreeableBytes() {
|
|
||||||
long freedBytes = 0;
|
|
||||||
for (Map.Entry<File, Boolean> entry : mFiles.entrySet()) {
|
|
||||||
if (entry.getValue()) {
|
|
||||||
freedBytes += entry.getKey().length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return freedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeUpdateListener() {
|
|
||||||
if (mListener != null) {
|
|
||||||
mListener.onFreeableChanged(mFiles.size(), mBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.CheckBoxPreference;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DownloadsFilePreference is a preference representing a file in the Downloads folder
|
|
||||||
* with a checkbox that represents if the file should be deleted.
|
|
||||||
*/
|
|
||||||
public class DownloadsFilePreference extends CheckBoxPreference {
|
|
||||||
private File mFile;
|
|
||||||
|
|
||||||
public DownloadsFilePreference(Context context, File file) {
|
|
||||||
super(context);
|
|
||||||
mFile = file;
|
|
||||||
setKey(mFile.getPath());
|
|
||||||
setTitle(file.getName());
|
|
||||||
setSummary(context.getString(R.string.deletion_helper_downloads_summary,
|
|
||||||
Formatter.formatFileSize(getContext(), file.length()),
|
|
||||||
DateUtils.getRelativeTimeSpanString(mFile.lastModified(),
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
DateUtils.DAY_IN_MILLIS,
|
|
||||||
DateUtils.FORMAT_ABBREV_RELATIVE)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getFile() {
|
|
||||||
return mFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(Preference other) {
|
|
||||||
if (other == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other instanceof DownloadsFilePreference) {
|
|
||||||
DownloadsFilePreference preference = (DownloadsFilePreference) other;
|
|
||||||
return Long.compare(getFile().length(), preference.getFile().length());
|
|
||||||
} else {
|
|
||||||
// If a non-DownloadsFilePreference appears, consider ourselves to be greater.
|
|
||||||
// This means if a non-DownloadsFilePreference sneaks into a DownloadsPreferenceGroup
|
|
||||||
// then the DownloadsFilePreference will appear higher.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
|
||||||
import com.android.settings.utils.AsyncLoader;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FetchDownloadsLoader is an asynchronous task which returns files in the Downloads
|
|
||||||
* directory which have not been modified in longer than 90 days.
|
|
||||||
*/
|
|
||||||
public class FetchDownloadsLoader extends
|
|
||||||
AsyncLoader<FetchDownloadsLoader.DownloadsResult> {
|
|
||||||
private File mDirectory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up a FetchDownloadsLoader in any directory.
|
|
||||||
* @param directory The directory to look into.
|
|
||||||
*/
|
|
||||||
public FetchDownloadsLoader(Context context, File directory) {
|
|
||||||
super(context);
|
|
||||||
mDirectory = directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDiscardResult(DownloadsResult result) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DownloadsResult loadInBackground() {
|
|
||||||
return collectFiles(mDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static DownloadsResult collectFiles(File dir) {
|
|
||||||
return collectFiles(dir, new DownloadsResult());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DownloadsResult collectFiles(File dir, DownloadsResult result) {
|
|
||||||
File downloadFiles[] = dir.listFiles();
|
|
||||||
if (downloadFiles == null) {
|
|
||||||
}
|
|
||||||
if (downloadFiles != null && downloadFiles.length > 0) {
|
|
||||||
for (File currentFile : downloadFiles) {
|
|
||||||
if (currentFile.isDirectory()) {
|
|
||||||
collectFiles(currentFile, result);
|
|
||||||
} else {
|
|
||||||
if (currentFile.lastModified() < result.youngestLastModified) {
|
|
||||||
result.youngestLastModified = currentFile.lastModified();
|
|
||||||
}
|
|
||||||
result.files.add(currentFile);
|
|
||||||
result.totalSize += currentFile.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The DownloadsResult is the result of a {@link FetchDownloadsLoader} with the files
|
|
||||||
* and the amount of space they use.
|
|
||||||
*/
|
|
||||||
public static class DownloadsResult {
|
|
||||||
public long totalSize;
|
|
||||||
public long youngestLastModified;
|
|
||||||
public ArrayList<File> files;
|
|
||||||
|
|
||||||
public DownloadsResult() {
|
|
||||||
this(0, Long.MAX_VALUE, new ArrayList<File>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownloadsResult(long totalSize, long youngestLastModified, ArrayList<File> files) {
|
|
||||||
this.totalSize = totalSize;
|
|
||||||
this.youngestLastModified = youngestLastModified;
|
|
||||||
this.files = files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,58 +0,0 @@
|
|||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.content.pm.IPackageDeleteObserver;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a specified set of apps as a specified user and calls back once done.
|
|
||||||
*/
|
|
||||||
public class PackageDeletionTask {
|
|
||||||
private Set<String> mPackages;
|
|
||||||
private Callback mCallback;
|
|
||||||
private PackageManager mPm;
|
|
||||||
private UserHandle mUser;
|
|
||||||
|
|
||||||
public PackageDeletionTask(PackageManager pm, Set<String> packageNames, Callback callback) {
|
|
||||||
mPackages = packageNames;
|
|
||||||
mCallback = callback;
|
|
||||||
mPm = pm;
|
|
||||||
mUser = android.os.Process.myUserHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
PackageDeletionObserver observer = new PackageDeletionObserver(mPackages.size());
|
|
||||||
for (String packageName : mPackages) {
|
|
||||||
mPm.deletePackageAsUser(packageName, observer, 0, mUser.getIdentifier());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PackageDeletionObserver extends IPackageDeleteObserver.Stub {
|
|
||||||
private final AtomicInteger mPackagesRemaining = new AtomicInteger(0);
|
|
||||||
|
|
||||||
public PackageDeletionObserver(int packages) {
|
|
||||||
mPackagesRemaining.set(packages);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void packageDeleted(String packageName, int returnCode) {
|
|
||||||
if (returnCode != PackageManager.DELETE_SUCCEEDED) {
|
|
||||||
mCallback.onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int remaining = mPackagesRemaining.decrementAndGet();
|
|
||||||
if (remaining == 0) {
|
|
||||||
mCallback.onSuccess();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static abstract class Callback {
|
|
||||||
public abstract void onSuccess();
|
|
||||||
public abstract void onError();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
|
||||||
* except in compliance with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the
|
|
||||||
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the specific language governing
|
|
||||||
* permissions and limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment for activating the storage manager after a manual clear.
|
|
||||||
*/
|
|
||||||
public class StorageManagerUpsellDialog extends DialogFragment
|
|
||||||
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
|
|
||||||
public static final String TAG = "StorageManagerUpsellDialog";
|
|
||||||
private static final String SHARED_PREFERENCES_NAME = "StorageManagerUpsellDialog";
|
|
||||||
private static final String NEXT_SHOW_TIME = "next_show_time";
|
|
||||||
private static final String DISMISSED_COUNT = "dismissed_count";
|
|
||||||
private static final String NO_THANKS_COUNT = "no_thanks_count";
|
|
||||||
|
|
||||||
private static final String ARGS_FREED_BYTES = "freed_bytes";
|
|
||||||
|
|
||||||
private static final long NEVER = -1;
|
|
||||||
private static final long DISMISS_SHORT_DELAY = TimeUnit.DAYS.toMillis(14);
|
|
||||||
private static final long DISMISS_LONG_DELAY = TimeUnit.DAYS.toMillis(90);
|
|
||||||
private static final int DISMISS_LONG_THRESHOLD = 9;
|
|
||||||
private static final long NO_THANKS_SHORT_DELAY = TimeUnit.DAYS.toMillis(90);
|
|
||||||
private static final long NO_THANKS_LONG_DELAY = NEVER;
|
|
||||||
private static final int NO_THANKS_LONG_THRESHOLD = 3;
|
|
||||||
|
|
||||||
public static StorageManagerUpsellDialog newInstance(long freedBytes) {
|
|
||||||
StorageManagerUpsellDialog dialog = new StorageManagerUpsellDialog();
|
|
||||||
Bundle args = new Bundle(1);
|
|
||||||
args.putLong(ARGS_FREED_BYTES, freedBytes);
|
|
||||||
dialog.setArguments(args);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final Bundle args = getArguments();
|
|
||||||
long freedBytes = args.getLong(ARGS_FREED_BYTES);
|
|
||||||
|
|
||||||
final Context context = getContext();
|
|
||||||
return new AlertDialog.Builder(context)
|
|
||||||
.setTitle(context.getString(R.string.deletion_helper_upsell_title,
|
|
||||||
Formatter.formatFileSize(context, freedBytes)))
|
|
||||||
.setMessage(context.getString(R.string.deletion_helper_upsell_summary))
|
|
||||||
.setPositiveButton(R.string.deletion_helper_upsell_activate, this)
|
|
||||||
.setNegativeButton(R.string.deletion_helper_upsell_cancel, this)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int buttonId) {
|
|
||||||
if (buttonId == DialogInterface.BUTTON_POSITIVE) {
|
|
||||||
// TODO: Activate the storage manager once the storage manager is landed.
|
|
||||||
} else {
|
|
||||||
SharedPreferences sp = getSharedPreferences(getContext());
|
|
||||||
int noThanksCount = sp.getInt(NO_THANKS_COUNT, 0) + 1;
|
|
||||||
SharedPreferences.Editor editor = sp.edit();
|
|
||||||
editor.putInt(NO_THANKS_COUNT, noThanksCount);
|
|
||||||
editor.putLong(NEXT_SHOW_TIME,
|
|
||||||
System.currentTimeMillis() + getNoThanksDelay(noThanksCount));
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
SharedPreferences sp = getSharedPreferences(getContext());
|
|
||||||
int dismissCount = sp.getInt(DISMISSED_COUNT, 0) + 1;
|
|
||||||
SharedPreferences.Editor editor = sp.edit();
|
|
||||||
editor.putInt(DISMISSED_COUNT, dismissCount);
|
|
||||||
editor.putLong(NEXT_SHOW_TIME,
|
|
||||||
System.currentTimeMillis() + getDismissDelay(dismissCount));
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if the dialog should be shown, given the delays between when it is shown.
|
|
||||||
* @param context Context to get shared preferences for determining the next show time.
|
|
||||||
*/
|
|
||||||
public static boolean shouldShow(Context context) {
|
|
||||||
// TODO: If the Storage Manager is enabled, return false.
|
|
||||||
long nextTimeToShow = getSharedPreferences(context).getLong(NEXT_SHOW_TIME, 0);
|
|
||||||
|
|
||||||
if (nextTimeToShow == NEVER) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return System.currentTimeMillis() > nextTimeToShow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SharedPreferences getSharedPreferences(Context context) {
|
|
||||||
return context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long getNoThanksDelay(int noThanksCount) {
|
|
||||||
return (noThanksCount > NO_THANKS_LONG_THRESHOLD)
|
|
||||||
? NO_THANKS_LONG_DELAY : NO_THANKS_SHORT_DELAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long getDismissDelay(int dismissCount) {
|
|
||||||
return (dismissCount > DISMISS_LONG_THRESHOLD)
|
|
||||||
? DISMISS_LONG_DELAY : DISMISS_SHORT_DELAY;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -38,6 +38,7 @@ import android.os.storage.StorageManager;
|
|||||||
import android.os.storage.VolumeInfo;
|
import android.os.storage.VolumeInfo;
|
||||||
import android.os.storage.VolumeRecord;
|
import android.os.storage.VolumeRecord;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.support.v7.preference.Preference;
|
import android.support.v7.preference.Preference;
|
||||||
import android.support.v7.preference.PreferenceCategory;
|
import android.support.v7.preference.PreferenceCategory;
|
||||||
import android.support.v7.preference.PreferenceGroup;
|
import android.support.v7.preference.PreferenceGroup;
|
||||||
@@ -59,9 +60,7 @@ import com.android.settings.Settings.StorageUseActivity;
|
|||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.applications.ManageApplications;
|
import com.android.settings.applications.ManageApplications;
|
||||||
import com.android.settings.deletionhelper.DeletionHelperFragment;
|
|
||||||
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
|
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
|
||||||
import com.android.settings.deletionhelper.StorageManagerUpsellDialog;
|
|
||||||
import com.android.settings.deviceinfo.StorageSettings.MountTask;
|
import com.android.settings.deviceinfo.StorageSettings.MountTask;
|
||||||
import com.android.settingslib.deviceinfo.StorageMeasurement;
|
import com.android.settingslib.deviceinfo.StorageMeasurement;
|
||||||
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementDetails;
|
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementDetails;
|
||||||
@@ -445,8 +444,9 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
case R.id.storage_free:
|
case R.id.storage_free:
|
||||||
startFragment(this, DeletionHelperFragment.class.getCanonicalName(),
|
final Intent deletion_helper_intent =
|
||||||
R.string.deletion_helper_title, DELETION_HELPER_SETTINGS, args);
|
new Intent(Settings.ACTION_DELETION_HELPER_SETTINGS);
|
||||||
|
startActivity(deletion_helper_intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
@@ -540,18 +540,6 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
|
|||||||
return super.onPreferenceTreeClick(pref);
|
return super.onPreferenceTreeClick(pref);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
if (requestCode == DELETION_HELPER_SETTINGS && resultCode == DELETION_HELPER_CLEAR &&
|
|
||||||
StorageManagerUpsellDialog.shouldShow(getActivity())) {
|
|
||||||
long freedBytes = data.getLongExtra(DeletionHelperFragment.FREED_BYTES_KEY, 0);
|
|
||||||
StorageManagerUpsellDialog dialog =
|
|
||||||
StorageManagerUpsellDialog.newInstance(freedBytes);
|
|
||||||
dialog.show(getFragmentManager(), StorageManagerUpsellDialog.TAG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
|
private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onDetailsChanged(MeasurementDetails details) {
|
public void onDetailsChanged(MeasurementDetails details) {
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.overlay;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v7.preference.PreferenceGroup;
|
|
||||||
import com.android.settings.deletionhelper.DeletionType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feature provider for the manual deletion helper Settings page.
|
|
||||||
*/
|
|
||||||
public interface DeletionHelperFeatureProvider {
|
|
||||||
/**
|
|
||||||
* Creates a {@link DeletionType} for clearing out stored photos and videos on the device.
|
|
||||||
*/
|
|
||||||
DeletionType createPhotoVideoDeletionType(Context context);
|
|
||||||
}
|
|
@@ -61,12 +61,6 @@ public abstract class FeatureFactory {
|
|||||||
|
|
||||||
public abstract SupportFeatureProvider getSupportFeatureProvider(Context context);
|
public abstract SupportFeatureProvider getSupportFeatureProvider(Context context);
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a provider which adds additional deletion services to the Deletion Helper.
|
|
||||||
*/
|
|
||||||
public abstract DeletionHelperFeatureProvider getDeletionHelperFeatureProvider();
|
|
||||||
public abstract StorageManagementJobProvider getStorageManagementJobProvider();
|
|
||||||
|
|
||||||
public static final class FactoryNotFoundException extends RuntimeException {
|
public static final class FactoryNotFoundException extends RuntimeException {
|
||||||
public FactoryNotFoundException(Throwable throwable) {
|
public FactoryNotFoundException(Throwable throwable) {
|
||||||
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
|
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
|
||||||
|
@@ -28,14 +28,4 @@ public final class FeatureFactoryImpl extends FeatureFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeletionHelperFeatureProvider getDeletionHelperFeatureProvider() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public StorageManagementJobProvider getStorageManagementJobProvider() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.overlay;
|
|
||||||
|
|
||||||
import android.app.job.JobParameters;
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feature provider for automatic storage management jobs.
|
|
||||||
*/
|
|
||||||
public interface StorageManagementJobProvider {
|
|
||||||
/**
|
|
||||||
* Starts an asynchronous deletion job to clear out storage older than
|
|
||||||
* @param params Standard JobService parameters.
|
|
||||||
* @param daysToRetain Number of days of information to retain on the device.
|
|
||||||
* @return If the job needs to process the work on a separate thread.
|
|
||||||
*/
|
|
||||||
boolean onStartJob(Context context, JobParameters params, int daysToRetain);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to stop the execution of the job.
|
|
||||||
* @param params Parameters specifying info about this job.
|
|
||||||
* @return If the job should be rescheduled.
|
|
||||||
*/
|
|
||||||
boolean onStopJob(Context context, JobParameters params);
|
|
||||||
}
|
|
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import com.android.settings.deletionhelper.FetchDownloadsLoader.DownloadsResult;
|
|
||||||
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.rules.TemporaryFolder;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class FetchDownloadsLoaderTest {
|
|
||||||
@Rule
|
|
||||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEmptyDirectory() throws Exception {
|
|
||||||
DownloadsResult result =
|
|
||||||
FetchDownloadsLoader.collectFiles(temporaryFolder.getRoot());
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(0, result.totalSize);
|
|
||||||
assertEquals(0, result.files.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFilesInDirectory() throws Exception {
|
|
||||||
temporaryFolder.newFile();
|
|
||||||
temporaryFolder.newFile();
|
|
||||||
|
|
||||||
DownloadsResult result =
|
|
||||||
FetchDownloadsLoader.collectFiles(temporaryFolder.getRoot());
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(0, result.totalSize);
|
|
||||||
assertEquals(2, result.files.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNestedDirectories() throws Exception {
|
|
||||||
File tempDir = temporaryFolder.newFolder();
|
|
||||||
|
|
||||||
File testFile = File.createTempFile("test", null, tempDir);
|
|
||||||
testFile.deleteOnExit();
|
|
||||||
DownloadsResult result =
|
|
||||||
FetchDownloadsLoader.collectFiles(temporaryFolder.getRoot());
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(0, result.totalSize);
|
|
||||||
assertEquals(1, result.files.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSumFileSizes() throws Exception {
|
|
||||||
File first = temporaryFolder.newFile();
|
|
||||||
FileWriter fileWriter = new FileWriter(first);
|
|
||||||
fileWriter.write("test");
|
|
||||||
fileWriter.close();
|
|
||||||
|
|
||||||
File second = temporaryFolder.newFile();
|
|
||||||
fileWriter = new FileWriter(second);
|
|
||||||
fileWriter.write("test2");
|
|
||||||
fileWriter.close();
|
|
||||||
|
|
||||||
DownloadsResult result =
|
|
||||||
FetchDownloadsLoader.collectFiles(temporaryFolder.getRoot());
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(9, result.totalSize);
|
|
||||||
assertEquals(2, result.files.size());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.deletionhelper;
|
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
|
||||||
import android.content.pm.IPackageDeleteObserver;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.test.mock.MockPackageManager;
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
|
||||||
|
|
||||||
import com.android.settings.deletionhelper.PackageDeletionTask;
|
|
||||||
import com.android.settings.deletionhelper.PackageDeletionTask.Callback;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
public class PackageDeletionTaskTest extends AndroidTestCase {
|
|
||||||
private FakePackageManager mPackageManager;
|
|
||||||
private Set<String> mDeletedApps;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
mPackageManager = new FakePackageManager();
|
|
||||||
mDeletedApps = new HashSet<String>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public void testDeleteNoApps() throws Exception {
|
|
||||||
runTask(new HashSet<String>(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public void testDeleteOneApp() throws Exception {
|
|
||||||
HashSet<String> appsToDelete = new HashSet<String>();
|
|
||||||
appsToDelete.add("app.test1");
|
|
||||||
runTask(appsToDelete, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public void testDeleteManyApps() throws Exception {
|
|
||||||
HashSet<String> appsToDelete = new HashSet<String>();
|
|
||||||
appsToDelete.add("app.test1");
|
|
||||||
appsToDelete.add("app.test2");
|
|
||||||
runTask(appsToDelete, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public void testDeleteFails() throws Exception {
|
|
||||||
HashSet<String> appsToDelete = new HashSet<String>();
|
|
||||||
appsToDelete.add("app.test1");
|
|
||||||
mPackageManager.deletionSucceeds = false;
|
|
||||||
runTask(appsToDelete, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runTask(HashSet<String> appsToDelete, boolean shouldFail) {
|
|
||||||
PackageDeletionTask task = new PackageDeletionTask(mPackageManager, appsToDelete,
|
|
||||||
new VerifierCallback(appsToDelete, shouldFail));
|
|
||||||
task.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakePackageManager extends MockPackageManager {
|
|
||||||
public boolean deletionSucceeds = true;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
|
|
||||||
int flags, int userId) {
|
|
||||||
int resultCode;
|
|
||||||
if (deletionSucceeds) {
|
|
||||||
resultCode = PackageManager.DELETE_SUCCEEDED;
|
|
||||||
mDeletedApps.add(packageName);
|
|
||||||
} else {
|
|
||||||
resultCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
observer.packageDeleted(packageName, resultCode);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
fail(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VerifierCallback extends Callback {
|
|
||||||
private Set<String> mExpectedDeletedApps;
|
|
||||||
private boolean mShouldFail;
|
|
||||||
|
|
||||||
public VerifierCallback(HashSet<String> expectedDeletedApps, boolean shouldFail) {
|
|
||||||
mExpectedDeletedApps = expectedDeletedApps;
|
|
||||||
mShouldFail = shouldFail;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
System.out.println("lol");
|
|
||||||
assertFalse(mShouldFail);
|
|
||||||
assertEquals(mExpectedDeletedApps, mDeletedApps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError() {
|
|
||||||
assertTrue(mShouldFail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user