Cleanup Storage Settings

Bug: 183078080
Test: make
      make RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.deviceinfo
      make RunSettingsRoboTests
      ROBOTEST_FILTER=com.android.settings.deviceinfo.storage
      atest com.android.settings.deviceinfo
      atest com.android.settings.deviceinfo.storage
Change-Id: I901f5f7999ea94dafb72a2517a11e2ff1fd51b42
This commit is contained in:
Arc Wang
2021-04-14 11:53:46 +08:00
parent bdaee1431f
commit 192fd24f26
40 changed files with 143 additions and 2997 deletions

View File

@@ -1979,13 +1979,6 @@
android:value="true" /> android:value="true" />
</activity> </activity>
<activity
android:name="Settings$PrivateVolumeSettingsActivity"
android:label="@string/storage_settings_title">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.deviceinfo.PrivateVolumeSettings" />
</activity>
<activity <activity
android:name="Settings$PublicVolumeSettingsActivity" android:name="Settings$PublicVolumeSettingsActivity"
android:exported="true" android:exported="true"

View File

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingStart="@dimen/preference_no_icon_padding_start"
android:paddingEnd="@dimen/storage_summary_padding_end"
android:paddingTop="32dp"
android:paddingBottom="32dp" >
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
android:textSize="36sp" />
<TextView
android:id="@android:id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:maxLines="10"
android:paddingBottom="20dp"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1" />
<Button
android:id="@+id/deletion_helper_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/summary"
android:text="@string/storage_menu_manage"
style="@style/ActionPrimaryButton" />
</LinearLayout>
<com.android.settings.widget.DonutView
android:id="@+id/donut"
android:layout_width="112dp"
android:layout_height="112dp"
android:layout_marginEnd="32dp"
android:gravity="end|center_vertical"
android:minWidth="58dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
</LinearLayout>

View File

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingStart="@dimen/preference_no_icon_padding_start"
android:paddingEnd="@dimen/storage_summary_padding_end"
android:paddingTop="32dp"
android:paddingBottom="32dp" >
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
android:textSize="36sp" />
<TextView
android:id="@android:id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:maxLines="10"
android:paddingBottom="20dp"
android:textAlignment="viewStart"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1" />
<Button
android:id="@+id/deletion_helper_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/summary"
android:text="@string/storage_menu_manage"
style="@style/ActionPrimaryButton" />
</LinearLayout>
<com.android.settings.widget.DonutView
android:id="@+id/donut"
android:layout_width="112dp"
android:layout_height="112dp"
android:layout_marginEnd="32dp"
android:gravity="end|center_vertical"
android:minWidth="58dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
</LinearLayout>

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/unmount"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:contentDescription="@string/storage_menu_unmount"
android:layout_gravity="center"
android:gravity="center"
android:src="@drawable/ic_eject_24dp"
android:background="?android:attr/selectableItemBackground" />
</LinearLayout>

View File

@@ -1,62 +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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="32dp"
android:paddingBottom="32dp" >
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textAlignment="center"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
android:textSize="36sp" />
<TextView
android:id="@android:id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:maxLines="10"
android:textAlignment="center"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1" />
<com.android.settings.widget.DonutView
android:id="@+id/donut"
android:layout_width="168dp"
android:layout_height="168dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:minWidth="58dp" />
<Button
android:id="@+id/deletion_helper_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/storage_menu_manage"
style="@style/ActionPrimaryButton" />
</LinearLayout>

View File

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/activatedBackgroundIndicator"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:minWidth="56dp"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="48dp"
android:maxHeight="48dp" />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
<TextView android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10" />
<ProgressBar
android:id="@android:id/progress"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_marginTop="8dp"
android:layout_below="@android:id/summary"
android:layout_alignStart="@android:id/summary"
android:max="100"
style="?android:attr/progressBarStyleHorizontal" />
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="end|center_vertical"
android:paddingStart="16dp"
android:orientation="vertical" />
</LinearLayout>

View File

@@ -29,12 +29,10 @@
android:title="@string/storage_menu_format" /> android:title="@string/storage_menu_format" />
<item <item
android:id="@+id/storage_format_as_portable" android:id="@+id/storage_format_as_portable"
android:title="@string/storage_menu_format_public" android:title="@string/storage_menu_format_public" />
android:visible="false" />
<item <item
android:id="@+id/storage_format_as_internal" android:id="@+id/storage_format_as_internal"
android:title="@string/storage_menu_format_private" android:title="@string/storage_menu_format_private" />
android:visible="false" />
<item <item
android:id="@+id/storage_migrate" android:id="@+id/storage_migrate"
android:title="@string/storage_menu_migrate" /> android:title="@string/storage_menu_migrate" />
@@ -43,6 +41,5 @@
android:title="@string/storage_menu_free" /> android:title="@string/storage_menu_free" />
<item <item
android:id="@+id/storage_forget" android:id="@+id/storage_forget"
android:title="@string/storage_menu_forget" android:title="@string/storage_menu_forget" />
android:visible="false" />
</menu> </menu>

View File

@@ -30,9 +30,4 @@
<!-- Display, Screen zoom --> <!-- Display, Screen zoom -->
<dimen name="screen_zoom_preview_height">160dp</dimen> <dimen name="screen_zoom_preview_height">160dp</dimen>
<!-- Text size of the big number in the donut. -->
<dimen name="storage_donut_view_percent_text_size">30sp</dimen>
<!-- Text size of the label text in the donut. -->
<dimen name="storage_donut_view_label_text_size">14sp</dimen>
</resources> </resources>

View File

@@ -25,8 +25,4 @@
<!-- Suggestion cards--> <!-- Suggestion cards-->
<dimen name="suggestion_card_padding_bottom_one_card">22dp</dimen> <dimen name="suggestion_card_padding_bottom_one_card">22dp</dimen>
<!-- Text size of the big number in the donut. -->
<dimen name="storage_donut_view_percent_text_size">30sp</dimen>
<!-- Text size of the label text in the donut. -->
<dimen name="storage_donut_view_label_text_size">14sp</dimen>
</resources> </resources>

View File

@@ -139,15 +139,6 @@
<attr name="android:gravity" /> <attr name="android:gravity" />
</declare-styleable> </declare-styleable>
<!-- For DonutView -->
<declare-styleable name="DonutView">
<attr name="meterBackgroundColor" format="color" />
<attr name="meterConsumedColor" format="color" />
<attr name="applyColorAccent" format="boolean" />
<attr name="showPercentString" format="boolean" />
<attr name="thickness" format="dimension" />
</declare-styleable>
<!-- For biometric enroll checkboxes --> <!-- For biometric enroll checkboxes -->
<declare-styleable name="BiometricEnrollCheckbox"> <declare-styleable name="BiometricEnrollCheckbox">
<attr name="icon" format="reference" /> <attr name="icon" format="reference" />

View File

@@ -319,19 +319,6 @@
<dimen name="select_dialog_item_margin_start">12dp</dimen> <dimen name="select_dialog_item_margin_start">12dp</dimen>
<dimen name="select_dialog_summary_padding_bottom">8dp</dimen> <dimen name="select_dialog_summary_padding_bottom">8dp</dimen>
<!-- Padding between the donut and the storage summary. -->
<dimen name="storage_summary_padding_end">16dp</dimen>
<!-- Text size of the big number in the donut. -->
<dimen name="storage_donut_view_percent_text_size">45sp</dimen>
<!-- Text size of the percentage sign in the donut. -->
<dimen name="storage_donut_view_percent_sign_size">20sp</dimen>
<!-- Text size of the label text in the donut. -->
<dimen name="storage_donut_view_label_text_size">21sp</dimen>
<!-- Text size of the label text in the donut if the label text is long. -->
<dimen name="storage_donut_view_shrunken_label_text_size">10sp</dimen>
<!-- The width of the storage summary donut -->
<dimen name="storage_donut_thickness">4dp</dimen>
<!-- Battery meter view size --> <!-- Battery meter view size -->
<dimen name="battery_meter_width">66dp</dimen> <dimen name="battery_meter_width">66dp</dimen>
<dimen name="battery_meter_height">100dp</dimen> <dimen name="battery_meter_height">100dp</dimen>

View File

@@ -3426,11 +3426,6 @@
<string name="sd_format_summary" product="default">Erases all data on the SD card, such as music and photos</string> <string name="sd_format_summary" product="default">Erases all data on the SD card, such as music and photos</string>
<!-- SD card status when it is mounted as read only. Will be appended to size, starts with an unbreakable space --> <!-- SD card status when it is mounted as read only. Will be appended to size, starts with an unbreakable space -->
<!-- Title of dialog asking user to confirm before clearing all caches. [CHAR LIMIT=48] -->
<string name="memory_clear_cache_title">Clear cached data?</string>
<!-- Message of dialog asking user to confirm before clearing all caches. [CHAR LIMIT=48] -->
<string name="memory_clear_cache_message">This will clear cached data for all apps.</string>
<!-- SD card & phone storage settings item summary that will result in the phone connected to PC and MTP/PTP enabled. [CHAR LIMIT=80] --> <!-- SD card & phone storage settings item summary that will result in the phone connected to PC and MTP/PTP enabled. [CHAR LIMIT=80] -->
<string name="mtp_ptp_mode_summary">MTP or PTP function is active</string> <string name="mtp_ptp_mode_summary">MTP or PTP function is active</string>
@@ -3482,8 +3477,6 @@
<string name="storage_menu_forget">Forget</string> <string name="storage_menu_forget">Forget</string>
<!-- Storage setting. Menu option for setting up a storage device [CHAR LIMIT=30]--> <!-- Storage setting. Menu option for setting up a storage device [CHAR LIMIT=30]-->
<string name="storage_menu_set_up">Set up</string> <string name="storage_menu_set_up">Set up</string>
<!-- Storage setting. Menu option for exploring a storage device [CHAR LIMIT=30]-->
<string name="storage_menu_explore">Explore</string>
<!-- Storage setting. Menu option for using the deletion helper. [CHAR LIMIT=30] --> <!-- Storage setting. Menu option for using the deletion helper. [CHAR LIMIT=30] -->
<string name="storage_menu_free">Free up space</string> <string name="storage_menu_free">Free up space</string>
<!-- Storage setting. Menu option for accessing the storage manager settings. [CHAR LIMIT=30] --> <!-- Storage setting. Menu option for accessing the storage manager settings. [CHAR LIMIT=30] -->
@@ -3520,14 +3513,10 @@
<!-- Section header above list of external storage devices [CHAR LIMIT=30]--> <!-- Section header above list of external storage devices [CHAR LIMIT=30]-->
<string name="storage_external_title">Portable storage</string> <string name="storage_external_title">Portable storage</string>
<!-- Summary of a single storage volume, constrasting available and total storage space. [CHAR LIMIT=48]-->
<string name="storage_volume_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> used of <xliff:g id="total" example="32GB">%2$s</xliff:g></string>
<!-- Summary of a single storage volume used space. [CHAR LIMIT=24] --> <!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
<string name="storage_size_large"><xliff:g id="number" example="128">^1</xliff:g><small><small> <xliff:g id="unit" example="KB">^2</xliff:g></small></small></string> <string name="storage_size_large"><xliff:g id="number" example="128">^1</xliff:g><small><small> <xliff:g id="unit" example="KB">^2</xliff:g></small></small></string>
<!-- Summary of a single storage volume total space. [CHAR LIMIT=48]--> <!-- Summary of a single storage volume total space. [CHAR LIMIT=48]-->
<string name="storage_volume_used">Used of <xliff:g id="total" example="32GB">%1$s</xliff:g></string> <string name="storage_volume_used">Used of <xliff:g id="total" example="32GB">%1$s</xliff:g></string>
<!-- Summary of a single storage volume total space. [CHAR LIMIT=48]-->
<string name="storage_volume_used_total">Total used of <xliff:g id="total" example="32GB">%1$s</xliff:g></string>
<!-- Toast informing that storage mount operation was successful. [CHAR LIMIT=64]--> <!-- Toast informing that storage mount operation was successful. [CHAR LIMIT=64]-->
<string name="storage_mount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is mounted</string> <string name="storage_mount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is mounted</string>
@@ -3555,10 +3544,6 @@
<string name="storage_dialog_unmountable">This <xliff:g id="name" example="SD card">^1</xliff:g> is corrupted. <string name="storage_dialog_unmountable">This <xliff:g id="name" example="SD card">^1</xliff:g> is corrupted.
\n\nTo use this <xliff:g id="name" example="SD card">^1</xliff:g>, you have to set it up first.</string> \n\nTo use this <xliff:g id="name" example="SD card">^1</xliff:g>, you have to set it up first.</string>
<!-- Dialog body informing user about an unsupported storage device. [CHAR LIMIT=NONE]-->
<string name="storage_dialog_unsupported">This device doesn\u2019t support this <xliff:g id="name" example="SD card">^1</xliff:g>.
\n\nTo use this <xliff:g id="name" example="SD card">^1</xliff:g> with this device, you have to set it up first.</string>
<!-- Body of dialog informing user about consequences of formatting an internal storage device [CHAR LIMIT=NONE]--> <!-- Body of dialog informing user about consequences of formatting an internal storage device [CHAR LIMIT=NONE]-->
<string name="storage_internal_format_details">After formatting, you can use this <xliff:g id="name" example="SD card">^1</xliff:g> in other devices. <string name="storage_internal_format_details">After formatting, you can use this <xliff:g id="name" example="SD card">^1</xliff:g> in other devices.
\n\nAll data on this <xliff:g id="name" example="SD card">^1</xliff:g> will be erased. Consider backing up first. \n\nAll data on this <xliff:g id="name" example="SD card">^1</xliff:g> will be erased. Consider backing up first.
@@ -3582,34 +3567,11 @@
<!-- Body of dialog confirming that user wants to forget an internal storage device [CHAR LIMIT=NONE]--> <!-- Body of dialog confirming that user wants to forget an internal storage device [CHAR LIMIT=NONE]-->
<string name="storage_internal_forget_confirm">All the apps, photos, and data stored on this <xliff:g id="name" example="SD card">^1</xliff:g> will be lost forever.</string> <string name="storage_internal_forget_confirm">All the apps, photos, and data stored on this <xliff:g id="name" example="SD card">^1</xliff:g> will be lost forever.</string>
<!-- Item title describing storage used by apps [CHAR LIMIT=48]-->
<string name="storage_detail_apps">Apps</string>
<!-- Item title describing storage used by images [CHAR LIMIT=48]--> <!-- Item title describing storage used by images [CHAR LIMIT=48]-->
<string name="storage_detail_images">Images</string> <string name="storage_detail_images">Images</string>
<!-- Item title describing storage used by videos [CHAR LIMIT=48]-->
<string name="storage_detail_videos">Videos</string>
<!-- Item title describing storage used by audio [CHAR LIMIT=48]-->
<string name="storage_detail_audio">Audio</string>
<!-- Item title describing storage used by cached data [CHAR LIMIT=48]-->
<string name="storage_detail_cached">Cached data</string>
<!-- Item title describing storage used by other data [CHAR LIMIT=48]-->
<string name="storage_detail_other">Other</string>
<!-- Item title describing internal storage used by the Android System [CHAR LIMIT=48]-->
<string name="storage_detail_system">System</string>
<!-- Item title that will launch a file explorer [CHAR LIMIT=48]-->
<string name="storage_detail_explore">Explore <xliff:g id="name" example="SD card">^1</xliff:g></string>
<!-- Body of dialog informing user about other files on a storage device [CHAR LIMIT=NONE]-->
<string name="storage_detail_dialog_other">Other includes shared files saved by apps, files downloaded from the internet or Bluetooth, Android files, and so on.
\n\nTo see the visible contents of this <xliff:g id="name" example="SD card">^1</xliff:g>, tap Explore.</string>
<!-- Body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]--> <!-- Body of dialog informing user about the storage used by the Android System [CHAR LIMIT=NONE]-->
<string name="storage_detail_dialog_system">System includes files used to run Android version <xliff:g id="version" example="8.0">%s</xliff:g></string> <string name="storage_detail_dialog_system">System includes files used to run Android version <xliff:g id="version" example="8.0">%s</xliff:g></string>
<!-- Body of dialog informing user about other users on a storage device [CHAR LIMIT=NONE]-->
<string name="storage_detail_dialog_user"><xliff:g id="user" example="Guest user">^1</xliff:g> may have saved photos, music, apps, or other data, using <xliff:g id="size" example="1.2 GB">^2</xliff:g> of storage.
\n\nTo view details, switch to <xliff:g id="user" example="Guest user">^1</xliff:g>.</string>
<!-- Title of wizard step prompting user to setup a storage device [CHAR LIMIT=32] --> <!-- Title of wizard step prompting user to setup a storage device [CHAR LIMIT=32] -->
<string name="storage_wizard_init_title">Set up your <xliff:g id="name" example="SD card">^1</xliff:g></string> <string name="storage_wizard_init_title">Set up your <xliff:g id="name" example="SD card">^1</xliff:g></string>
<!-- Title of wizard choice to use storage device as external storage [CHAR LIMIT=64] --> <!-- Title of wizard choice to use storage device as external storage [CHAR LIMIT=64] -->

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/storage_settings"
settings:keywords="@string/keywords_storage">
<PreferenceCategory
android:key="storage_internal"
android:title="@string/storage_internal_title" />
<PreferenceCategory
android:key="storage_external"
android:title="@string/storage_external_title" />
</PreferenceScreen>

View File

@@ -17,7 +17,6 @@
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto" xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/storage_settings" android:title="@string/storage_settings">
settings:keywords="@string/keywords_storage">
</PreferenceScreen> </PreferenceScreen>

View File

@@ -18,7 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto" xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/storage_settings" android:title="@string/storage_settings"
android:orderingFromXml="false"> android:orderingFromXml="false"
settings:keywords="@string/keywords_storage">
<com.android.settingslib.widget.SettingsSpinnerPreference <com.android.settingslib.widget.SettingsSpinnerPreference
android:key="storage_spinner" android:key="storage_spinner"
android:order="1" android:order="1"

View File

@@ -79,7 +79,6 @@ public class Settings extends SettingsActivity {
public static class DataSaverSummaryActivity extends SettingsActivity{ /* empty */ } public static class DataSaverSummaryActivity extends SettingsActivity{ /* empty */ }
public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ } public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }
public static class PrivateVolumeForgetActivity extends SettingsActivity { /* empty */ } public static class PrivateVolumeForgetActivity extends SettingsActivity { /* empty */ }
public static class PrivateVolumeSettingsActivity extends SettingsActivity { /* empty */ }
public static class PublicVolumeSettingsActivity extends SettingsActivity { /* empty */ } public static class PublicVolumeSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiSettingsActivity extends SettingsActivity { /* empty */ } public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiSettings2Activity extends SettingsActivity { /* empty */ } public static class WifiSettings2Activity extends SettingsActivity { /* empty */ }

View File

@@ -78,10 +78,8 @@ import com.android.settings.datetime.DateTimeSettings;
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings; import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
import com.android.settings.development.DevelopmentSettingsDashboardFragment; import com.android.settings.development.DevelopmentSettingsDashboardFragment;
import com.android.settings.deviceinfo.PrivateVolumeForget; import com.android.settings.deviceinfo.PrivateVolumeForget;
import com.android.settings.deviceinfo.PrivateVolumeSettings;
import com.android.settings.deviceinfo.PublicVolumeSettings; import com.android.settings.deviceinfo.PublicVolumeSettings;
import com.android.settings.deviceinfo.StorageDashboardFragment; import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment; import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings; import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings;
import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard; import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard;
@@ -215,9 +213,7 @@ public class SettingsGateway {
ToggleDaltonizerPreferenceFragment.class.getName(), ToggleDaltonizerPreferenceFragment.class.getName(),
ToggleReduceBrightColorsPreferenceFragment.class.getName(), ToggleReduceBrightColorsPreferenceFragment.class.getName(),
TextToSpeechSettings.class.getName(), TextToSpeechSettings.class.getName(),
StorageSettings.class.getName(),
PrivateVolumeForget.class.getName(), PrivateVolumeForget.class.getName(),
PrivateVolumeSettings.class.getName(),
PublicVolumeSettings.class.getName(), PublicVolumeSettings.class.getName(),
DevelopmentSettingsDashboardFragment.class.getName(), DevelopmentSettingsDashboardFragment.class.getName(),
AndroidBeam.class.getName(), AndroidBeam.class.getName(),

View File

@@ -16,8 +16,6 @@
package com.android.settings.deviceinfo; package com.android.settings.deviceinfo;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.app.usage.ExternalStorageStats; import android.app.usage.ExternalStorageStats;
import android.app.usage.StorageStatsManager; import android.app.usage.StorageStatsManager;
import android.content.Context; import android.content.Context;
@@ -37,6 +35,8 @@ import java.io.IOException;
import java.util.UUID; import java.util.UUID;
public abstract class MigrateEstimateTask extends AsyncTask<Void, Void, Long> { public abstract class MigrateEstimateTask extends AsyncTask<Void, Void, Long> {
private static final String TAG = "MigrateEstimateTask";
private static final String EXTRA_SIZE_BYTES = "size_bytes"; private static final String EXTRA_SIZE_BYTES = "size_bytes";
/** /**

View File

@@ -1,898 +0,0 @@
/*
* Copyright (C) 2015 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.deviceinfo;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.provider.DocumentsContract;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.format.Formatter.BytesResult;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Settings.StorageUseActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.deviceinfo.StorageSettings.MountTask;
import com.android.settingslib.deviceinfo.StorageMeasurement;
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementDetails;
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementReceiver;
import com.google.android.collect.Lists;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
/**
* Panel showing summary and actions for a {@link VolumeInfo#TYPE_PRIVATE}
* storage volume.
*/
public class PrivateVolumeSettings extends SettingsPreferenceFragment {
// TODO: disable unmount when providing over MTP/PTP
// TODO: warn when mounted read-only
private static final String TAG = "PrivateVolumeSettings";
private static final boolean LOGV = false;
private static final String TAG_RENAME = "rename";
private static final String TAG_OTHER_INFO = "otherInfo";
private static final String TAG_SYSTEM_INFO = "systemInfo";
private static final String TAG_USER_INFO = "userInfo";
private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache";
private static final String EXTRA_VOLUME_SIZE = "volume_size";
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
private static final int[] ITEMS_NO_SHOW_SHARED = new int[] {
R.string.storage_detail_apps,
R.string.storage_detail_system,
};
private static final int[] ITEMS_SHOW_SHARED = new int[] {
R.string.storage_detail_apps,
R.string.storage_detail_images,
R.string.storage_detail_videos,
R.string.storage_detail_audio,
R.string.storage_detail_system,
R.string.storage_detail_other,
};
private StorageManager mStorageManager;
private UserManager mUserManager;
private String mVolumeId;
private VolumeInfo mVolume;
private VolumeInfo mSharedVolume;
private long mTotalSize;
private long mSystemSize;
private StorageMeasurement mMeasure;
private UserInfo mCurrentUser;
private StorageSummaryPreference mSummary;
private List<StorageItemPreference> mItemPreferencePool = Lists.newArrayList();
private List<PreferenceCategory> mHeaderPreferencePool = Lists.newArrayList();
private int mHeaderPoolIndex;
private int mItemPoolIndex;
private Preference mExplore;
private boolean mNeedsUpdate;
private boolean isVolumeValid() {
return (mVolume != null) && (mVolume.getType() == VolumeInfo.TYPE_PRIVATE)
&& mVolume.isMountedReadable();
}
public PrivateVolumeSettings() {
setRetainInstance(true);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DEVICEINFO_STORAGE;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getActivity();
mUserManager = context.getSystemService(UserManager.class);
mStorageManager = context.getSystemService(StorageManager.class);
mVolumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
mVolume = mStorageManager.findVolumeById(mVolumeId);
final long sharedDataSize = mVolume.getPath().getTotalSpace();
mTotalSize = getArguments().getLong(EXTRA_VOLUME_SIZE, 0);
mSystemSize = mTotalSize - sharedDataSize;
if (LOGV) Log.v(TAG,
"onCreate() mTotalSize: " + mTotalSize + " sharedDataSize: " + sharedDataSize);
if (mTotalSize <= 0) {
mTotalSize = sharedDataSize;
mSystemSize = 0;
}
// Find the emulated shared storage layered above this private volume
mSharedVolume = mStorageManager.findEmulatedForPrivate(mVolume);
mMeasure = new StorageMeasurement(context, mVolume, mSharedVolume);
mMeasure.setReceiver(mReceiver);
if (!isVolumeValid()) {
getActivity().finish();
return;
}
addPreferencesFromResource(R.xml.device_info_storage_volume);
getPreferenceScreen().setOrderingAsAdded(true);
mSummary = new StorageSummaryPreference(getPrefContext());
mCurrentUser = mUserManager.getUserInfo(UserHandle.myUserId());
mExplore = buildAction(R.string.storage_menu_explore);
mNeedsUpdate = true;
setHasOptionsMenu(true);
}
private void setTitle() {
getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume));
}
private void update() {
if (!isVolumeValid()) {
getActivity().finish();
return;
}
setTitle();
// Valid options may have changed
getActivity().invalidateOptionsMenu();
final Context context = getActivity();
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
addPreference(screen, mSummary);
List<UserInfo> allUsers = mUserManager.getUsers();
final int userCount = allUsers.size();
final boolean showHeaders = userCount > 1;
final boolean showShared = (mSharedVolume != null) && mSharedVolume.isMountedReadable();
mItemPoolIndex = 0;
mHeaderPoolIndex = 0;
int addedUserCount = 0;
// Add current user and its profiles first
for (int userIndex = 0; userIndex < userCount; ++userIndex) {
final UserInfo userInfo = allUsers.get(userIndex);
if (Utils.isProfileOf(mCurrentUser, userInfo)) {
final PreferenceGroup details = showHeaders ?
addCategory(screen, userInfo.name) : screen;
addDetailItems(details, showShared, userInfo.id);
++addedUserCount;
}
}
// Add rest of users
if (userCount - addedUserCount > 0) {
PreferenceGroup otherUsers = addCategory(screen,
getText(R.string.storage_other_users));
for (int userIndex = 0; userIndex < userCount; ++userIndex) {
final UserInfo userInfo = allUsers.get(userIndex);
if (!Utils.isProfileOf(mCurrentUser, userInfo)) {
addItem(otherUsers, /* titleRes */ 0, userInfo.name, userInfo.id);
}
}
}
addItem(screen, R.string.storage_detail_cached, null, UserHandle.USER_NULL);
if (showShared) {
addPreference(screen, mExplore);
}
final long freeBytes = mVolume.getPath().getFreeSpace();
final long usedBytes = mTotalSize - freeBytes;
if (LOGV) Log.v(TAG, "update() freeBytes: " + freeBytes + " usedBytes: " + usedBytes);
final BytesResult result = Formatter.formatBytes(getResources(), usedBytes, 0);
mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
result.value, result.units));
mSummary.setSummary(getString(R.string.storage_volume_used,
Formatter.formatFileSize(context, mTotalSize)));
mSummary.setPercent(usedBytes, mTotalSize);
mMeasure.forceMeasure();
mNeedsUpdate = false;
}
private void addPreference(PreferenceGroup group, Preference pref) {
pref.setOrder(Preference.DEFAULT_ORDER);
group.addPreference(pref);
}
private PreferenceCategory addCategory(PreferenceGroup group, CharSequence title) {
PreferenceCategory category;
if (mHeaderPoolIndex < mHeaderPreferencePool.size()) {
category = mHeaderPreferencePool.get(mHeaderPoolIndex);
} else {
category = new PreferenceCategory(getPrefContext());
mHeaderPreferencePool.add(category);
}
category.setTitle(title);
category.removeAll();
addPreference(group, category);
++mHeaderPoolIndex;
return category;
}
private void addDetailItems(PreferenceGroup category, boolean showShared, int userId) {
final int[] itemsToAdd = (showShared ? ITEMS_SHOW_SHARED : ITEMS_NO_SHOW_SHARED);
for (int i = 0; i < itemsToAdd.length; ++i) {
addItem(category, itemsToAdd[i], null, userId);
}
}
private void addItem(PreferenceGroup group, int titleRes, CharSequence title, int userId) {
if (titleRes == R.string.storage_detail_system) {
if (mSystemSize <= 0) {
Log.w(TAG, "Skipping System storage because its size is " + mSystemSize);
return;
}
if (userId != UserHandle.myUserId()) {
// Only display system on current user.
return;
}
}
StorageItemPreference item;
if (mItemPoolIndex < mItemPreferencePool.size()) {
item = mItemPreferencePool.get(mItemPoolIndex);
} else {
item = buildItem();
mItemPreferencePool.add(item);
}
if (title != null) {
item.setTitle(title);
item.setKey(title.toString());
} else {
item.setTitle(titleRes);
item.setKey(Integer.toString(titleRes));
}
item.setSummary(R.string.memory_calculating_size);
item.userHandle = userId;
addPreference(group, item);
++mItemPoolIndex;
}
private StorageItemPreference buildItem() {
final StorageItemPreference item = new StorageItemPreference(getPrefContext());
item.setIcon(R.drawable.empty_icon);
return item;
}
private Preference buildAction(int titleRes) {
final Preference pref = new Preference(getPrefContext());
pref.setTitle(titleRes);
pref.setKey(Integer.toString(titleRes));
return pref;
}
static void setVolumeSize(Bundle args, long size) {
args.putLong(EXTRA_VOLUME_SIZE, size);
}
@Override
public void onResume() {
super.onResume();
// Refresh to verify that we haven't been formatted away
mVolume = mStorageManager.findVolumeById(mVolumeId);
if (!isVolumeValid()) {
getActivity().finish();
return;
}
mStorageManager.registerListener(mStorageListener);
if (mNeedsUpdate) {
update();
} else {
setTitle();
}
}
@Override
public void onPause() {
super.onPause();
mStorageManager.unregisterListener(mStorageListener);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mMeasure != null) {
mMeasure.onDestroy();
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.storage_volume, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
if (!isVolumeValid()) return;
final MenuItem rename = menu.findItem(R.id.storage_rename);
final MenuItem mount = menu.findItem(R.id.storage_mount);
final MenuItem unmount = menu.findItem(R.id.storage_unmount);
final MenuItem format = menu.findItem(R.id.storage_format);
final MenuItem migrate = menu.findItem(R.id.storage_migrate);
final MenuItem manage = menu.findItem(R.id.storage_free);
// Actions live in menu for non-internal private volumes; they're shown
// as preference items for public volumes.
if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(mVolume.getId())) {
rename.setVisible(false);
mount.setVisible(false);
unmount.setVisible(false);
format.setVisible(false);
manage.setVisible(getResources().getBoolean(
R.bool.config_storage_manager_settings_enabled));
} else {
rename.setVisible(mVolume.getType() == VolumeInfo.TYPE_PRIVATE);
mount.setVisible(mVolume.getState() == VolumeInfo.STATE_UNMOUNTED);
unmount.setVisible(mVolume.isMountedReadable());
format.setVisible(true);
manage.setVisible(false);
}
format.setTitle(R.string.storage_menu_format_public);
// Only offer to migrate when not current storage
final VolumeInfo privateVol = getActivity().getPackageManager()
.getPrimaryStorageCurrentVolume();
migrate.setVisible((privateVol != null)
&& (privateVol.getType() == VolumeInfo.TYPE_PRIVATE)
&& !Objects.equals(mVolume, privateVol)
&& privateVol.isMountedWritable());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final Context context = getActivity();
final Bundle args = new Bundle();
int i = item.getItemId();
if (i == R.id.storage_rename) {
RenameFragment.show(this, mVolume);
return true;
} else if (i == R.id.storage_mount) {
new MountTask(context, mVolume).execute();
return true;
} else if (i == R.id.storage_unmount) {
args.putString(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
new SubSettingLauncher(context)
.setDestination(PrivateVolumeUnmount.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_unmount)
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args)
.launch();
return true;
} else if (i == R.id.storage_format) {
args.putString(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
new SubSettingLauncher(context)
.setDestination(PrivateVolumeFormat.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_format)
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args)
.launch();
return true;
} else if (i == R.id.storage_migrate) {
final Intent intent = new Intent(context, StorageWizardMigrateConfirm.class);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
startActivity(intent);
return true;
} else if (i == R.id.storage_free) {
final Intent deletion_helper_intent =
new Intent(StorageManager.ACTION_MANAGE_STORAGE);
startActivity(deletion_helper_intent);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onPreferenceTreeClick(Preference pref) {
// TODO: launch better intents for specific volume
final int userId = (pref instanceof StorageItemPreference ?
((StorageItemPreference) pref).userHandle : -1);
int itemTitleId;
try {
itemTitleId = Integer.parseInt(pref.getKey());
} catch (NumberFormatException e) {
itemTitleId = 0;
}
Intent intent = null;
if (itemTitleId == R.string.storage_detail_apps) {
Bundle args = new Bundle();
args.putString(ManageApplications.EXTRA_CLASSNAME,
StorageUseActivity.class.getName());
args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
args.putInt(
ManageApplications.EXTRA_STORAGE_TYPE,
ManageApplications.STORAGE_TYPE_LEGACY);
intent = new SubSettingLauncher(getActivity())
.setDestination(ManageApplications.class.getName())
.setArguments(args)
.setTitleRes(R.string.apps_storage)
.setSourceMetricsCategory(getMetricsCategory())
.toIntent();
} else if (itemTitleId == R.string.storage_detail_images) {
intent = getIntentForStorage(AUTHORITY_MEDIA, "images_root");
} else if (itemTitleId == R.string.storage_detail_videos) {
intent = getIntentForStorage(AUTHORITY_MEDIA, "videos_root");
} else if (itemTitleId == R.string.storage_detail_audio) {
intent = getIntentForStorage(AUTHORITY_MEDIA, "audio_root");
} else if (itemTitleId == R.string.storage_detail_system) {
SystemInfoFragment.show(this);
return true;
} else if (itemTitleId == R.string.storage_detail_other) {
OtherInfoFragment.show(this, mStorageManager.getBestVolumeDescription(mVolume),
mSharedVolume, userId);
return true;
} else if (itemTitleId == R.string.storage_detail_cached) {
ConfirmClearCacheFragment.show(this);
return true;
} else if (itemTitleId == R.string.storage_menu_explore) {
intent = mSharedVolume.buildBrowseIntent();
} else if (itemTitleId == 0) {
UserInfoFragment.show(this, pref.getTitle(), pref.getSummary());
return true;
}
if (intent != null) {
intent.putExtra(Intent.EXTRA_USER_ID, userId);
Utils.launchIntent(this, intent);
return true;
}
return super.onPreferenceTreeClick(pref);
}
private Intent getIntentForStorage(String authority, String root) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
DocumentsContract.buildRootUri(authority, root),
DocumentsContract.Root.MIME_TYPE_ITEM);
intent.addCategory(Intent.CATEGORY_DEFAULT);
return intent;
}
private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
@Override
public void onDetailsChanged(MeasurementDetails details) {
updateDetails(details);
}
};
private void updateDetails(MeasurementDetails details) {
StorageItemPreference otherItem = null;
long accountedSize = 0;
long totalMiscSize = 0;
long totalDownloadsSize = 0;
for (int i = 0; i < mItemPoolIndex; ++i) {
StorageItemPreference item = mItemPreferencePool.get(i);
final int userId = item.userHandle;
int itemTitleId;
try {
itemTitleId = Integer.parseInt(item.getKey());
} catch (NumberFormatException e) {
itemTitleId = 0;
}
// Cannot display 'Other' until all known items are accounted for.
if (itemTitleId == R.string.storage_detail_system) {
updatePreference(item, mSystemSize);
accountedSize += mSystemSize;
if (LOGV) Log.v(TAG, "mSystemSize: " + mSystemSize
+ " accountedSize: " + accountedSize);
} else if (itemTitleId == R.string.storage_detail_apps) {
updatePreference(item, details.appsSize.get(userId));
accountedSize += details.appsSize.get(userId);
if (LOGV) Log.v(TAG, "appsSize: " + details.appsSize.get(userId)
+ " accountedSize: " + accountedSize);
} else if (itemTitleId == R.string.storage_detail_images) {
final long imagesSize = totalValues(details, userId,
Environment.DIRECTORY_DCIM, Environment.DIRECTORY_PICTURES);
updatePreference(item, imagesSize);
accountedSize += imagesSize;
if (LOGV) Log.v(TAG, "imagesSize: " + imagesSize
+ " accountedSize: " + accountedSize);
} else if (itemTitleId == R.string.storage_detail_videos) {
final long videosSize = totalValues(details, userId,
Environment.DIRECTORY_MOVIES);
updatePreference(item, videosSize);
accountedSize += videosSize;
if (LOGV) Log.v(TAG, "videosSize: " + videosSize
+ " accountedSize: " + accountedSize);
} else if (itemTitleId == R.string.storage_detail_audio) {
final long audioSize = totalValues(details, userId,
Environment.DIRECTORY_MUSIC,
Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
updatePreference(item, audioSize);
accountedSize += audioSize;
if (LOGV) Log.v(TAG, "audioSize: " + audioSize
+ " accountedSize: " + accountedSize);
} else if (itemTitleId == R.string.storage_detail_other) {
final long downloadsSize = totalValues(details, userId,
Environment.DIRECTORY_DOWNLOADS);
final long miscSize = details.miscSize.get(userId);
totalDownloadsSize += downloadsSize;
totalMiscSize += miscSize;
accountedSize += miscSize + downloadsSize;
if (LOGV)
Log.v(TAG, "miscSize for " + userId + ": " + miscSize + "(total: "
+ totalMiscSize + ") \ndownloadsSize: " + downloadsSize + "(total: "
+ totalDownloadsSize + ") accountedSize: " + accountedSize);
otherItem = item;
} else if (itemTitleId == R.string.storage_detail_cached) {
updatePreference(item, details.cacheSize);
accountedSize += details.cacheSize;
if (LOGV)
Log.v(TAG, "cacheSize: " + details.cacheSize + " accountedSize: "
+ accountedSize);
} else if (itemTitleId == 0) {
final long userSize = details.usersSize.get(userId);
updatePreference(item, userSize);
accountedSize += userSize;
if (LOGV) Log.v(TAG, "userSize: " + userSize
+ " accountedSize: " + accountedSize);
}
}
if (otherItem != null) {
final long usedSize = mTotalSize - details.availSize;
final long unaccountedSize = usedSize - accountedSize;
final long otherSize = totalMiscSize + totalDownloadsSize + unaccountedSize;
Log.v(TAG, "Other items: \n\tmTotalSize: " + mTotalSize + " availSize: "
+ details.availSize + " usedSize: " + usedSize + "\n\taccountedSize: "
+ accountedSize + " unaccountedSize size: " + unaccountedSize
+ "\n\ttotalMiscSize: " + totalMiscSize + " totalDownloadsSize: "
+ totalDownloadsSize + "\n\tdetails: " + details);
updatePreference(otherItem, otherSize);
}
}
private void updatePreference(StorageItemPreference pref, long size) {
pref.setStorageSize(size, mTotalSize);
}
private static long totalValues(MeasurementDetails details, int userId, String... keys) {
long total = 0;
HashMap<String, Long> map = details.mediaSize.get(userId);
if (map != null) {
for (String key : keys) {
if (map.containsKey(key)) {
total += map.get(key);
}
}
} else {
Log.w(TAG, "MeasurementDetails mediaSize array does not have key for user " + userId);
}
return total;
}
private final StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
if (Objects.equals(mVolume.getId(), vol.getId())) {
mVolume = vol;
update();
}
}
@Override
public void onVolumeRecordChanged(VolumeRecord rec) {
if (Objects.equals(mVolume.getFsUuid(), rec.getFsUuid())) {
mVolume = mStorageManager.findVolumeById(mVolumeId);
update();
}
}
};
/**
* Dialog that allows editing of volume nickname.
*/
public static class RenameFragment extends InstrumentedDialogFragment {
public static void show(PrivateVolumeSettings parent, VolumeInfo vol) {
if (!parent.isAdded()) return;
final RenameFragment dialog = new RenameFragment();
dialog.setTargetFragment(parent, 0);
final Bundle args = new Bundle();
args.putString(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid());
dialog.setArguments(args);
dialog.show(parent.getFragmentManager(), TAG_RENAME);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_VOLUME_RENAME;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final StorageManager storageManager = context.getSystemService(StorageManager.class);
final String fsUuid = getArguments().getString(VolumeRecord.EXTRA_FS_UUID);
final VolumeInfo vol = storageManager.findVolumeByUuid(fsUuid);
final VolumeRecord rec = storageManager.findRecordByUuid(fsUuid);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
final View view = dialogInflater.inflate(R.layout.dialog_edittext, null, false);
final EditText nickname = (EditText) view.findViewById(R.id.edittext);
nickname.setText(rec.getNickname());
builder.setTitle(R.string.storage_rename_title);
builder.setView(view);
builder.setPositiveButton(R.string.save,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO: move to background thread
storageManager.setVolumeNickname(fsUuid,
nickname.getText().toString());
}
});
builder.setNegativeButton(R.string.cancel, null);
return builder.create();
}
}
public static class SystemInfoFragment extends InstrumentedDialogFragment {
public static void show(Fragment parent) {
if (!parent.isAdded()) return;
final SystemInfoFragment dialog = new SystemInfoFragment();
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_SYSTEM_INFO);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_STORAGE_SYSTEM_INFO;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(getContext().getString(R.string.storage_detail_dialog_system,
Build.VERSION.RELEASE_OR_CODENAME))
.setPositiveButton(android.R.string.ok, null)
.create();
}
}
public static class OtherInfoFragment extends InstrumentedDialogFragment {
public static void show(Fragment parent, String title, VolumeInfo sharedVol, int userId) {
if (!parent.isAdded()) return;
final OtherInfoFragment dialog = new OtherInfoFragment();
dialog.setTargetFragment(parent, 0);
final Bundle args = new Bundle();
args.putString(Intent.EXTRA_TITLE, title);
final Intent intent = sharedVol.buildBrowseIntent();
intent.putExtra(Intent.EXTRA_USER_ID, userId);
args.putParcelable(Intent.EXTRA_INTENT, intent);
dialog.setArguments(args);
dialog.show(parent.getFragmentManager(), TAG_OTHER_INFO);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_STORAGE_OTHER_INFO;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final String title = getArguments().getString(Intent.EXTRA_TITLE);
final Intent intent = getArguments().getParcelable(Intent.EXTRA_INTENT);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(
TextUtils.expandTemplate(getText(R.string.storage_detail_dialog_other), title));
builder.setPositiveButton(R.string.storage_menu_explore,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Utils.launchIntent(OtherInfoFragment.this, intent);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
return builder.create();
}
}
public static class UserInfoFragment extends InstrumentedDialogFragment {
public static void show(Fragment parent, CharSequence userLabel, CharSequence userSize) {
if (!parent.isAdded()) return;
final UserInfoFragment dialog = new UserInfoFragment();
dialog.setTargetFragment(parent, 0);
final Bundle args = new Bundle();
args.putCharSequence(Intent.EXTRA_TITLE, userLabel);
args.putCharSequence(Intent.EXTRA_SUBJECT, userSize);
dialog.setArguments(args);
dialog.show(parent.getFragmentManager(), TAG_USER_INFO);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_STORAGE_USER_INFO;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final CharSequence userLabel = getArguments().getCharSequence(Intent.EXTRA_TITLE);
final CharSequence userSize = getArguments().getCharSequence(Intent.EXTRA_SUBJECT);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(TextUtils.expandTemplate(
getText(R.string.storage_detail_dialog_user), userLabel, userSize));
builder.setPositiveButton(android.R.string.ok, null);
return builder.create();
}
}
/**
* Dialog to request user confirmation before clearing all cache data.
*/
public static class ConfirmClearCacheFragment extends InstrumentedDialogFragment {
public static void show(Fragment parent) {
if (!parent.isAdded()) return;
final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment();
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_STORAGE_CLEAR_CACHE;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.memory_clear_cache_title);
builder.setMessage(getString(R.string.memory_clear_cache_message));
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final PrivateVolumeSettings target = (PrivateVolumeSettings) getTargetFragment();
final PackageManager pm = context.getPackageManager();
final UserManager um = context.getSystemService(UserManager.class);
for (int userId : um.getProfileIdsWithDisabled(context.getUserId())) {
final List<PackageInfo> infos = pm.getInstalledPackagesAsUser(0, userId);
final ClearCacheObserver observer = new ClearCacheObserver(
target, infos.size());
for (PackageInfo info : infos) {
pm.deleteApplicationCacheFilesAsUser(info.packageName, userId,
observer);
}
}
}
});
builder.setNegativeButton(android.R.string.cancel, null);
return builder.create();
}
}
private static class ClearCacheObserver extends IPackageDataObserver.Stub {
private final PrivateVolumeSettings mTarget;
private int mRemaining;
public ClearCacheObserver(PrivateVolumeSettings target, int remaining) {
mTarget = target;
mRemaining = remaining;
}
@Override
public void onRemoveCompleted(final String packageName, final boolean succeeded) {
synchronized (this) {
if (--mRemaining == 0) {
mTarget.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mTarget.update();
}
});
}
}
}
}
}

View File

@@ -31,7 +31,7 @@ import android.widget.TextView;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.InstrumentedFragment;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask; import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
import com.android.settings.search.actionbar.SearchMenuController; import com.android.settings.search.actionbar.SearchMenuController;
public class PrivateVolumeUnmount extends InstrumentedFragment { public class PrivateVolumeUnmount extends InstrumentedFragment {

View File

@@ -40,8 +40,8 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.deviceinfo.StorageSettings.MountTask; import com.android.settings.deviceinfo.storage.StorageUtils.MountTask;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask; import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
import java.io.File; import java.io.File;
import java.util.Objects; import java.util.Objects;

View File

@@ -1,692 +0,0 @@
/*
* Copyright (C) 2015 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.deviceinfo;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.DiskInfo;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.format.Formatter.BytesResult;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Panel showing both internal storage (both built-in storage and private
* volumes) and removable storage (public volumes).
*/
@SearchIndexable
public class StorageSettings extends SettingsPreferenceFragment implements Indexable {
static final String TAG = "StorageSettings";
private static final String KEY_STORAGE_SETTINGS = "storage_settings";
private static final String KEY_INTERNAL_STORAGE = "storage_settings_internal_storage";
private static final String KEY_STORAGE_SETTINGS_VOLUME = "storage_settings_volume_";
private static final String KEY_STORAGE_SETTINGS_MEMORY_SIZE = "storage_settings_memory_size";
private static final String KEY_STORAGE_SETTINGS_MEMORY = "storage_settings_memory_available";
private static final String KEY_STORAGE_SETTINGS_DCIM = "storage_settings_dcim_space";
private static final String KEY_STORAGE_SETTINGS_MUSIC = "storage_settings_music_space";
private static final String KEY_STORAGE_SETTINGS_MISC = "storage_settings_misc_space";
private static final String KEY_STORAGE_SETTINGS_FREE_SPACE = "storage_settings_free_space";
private static final String TAG_VOLUME_UNMOUNTED = "volume_unmounted";
private static final String TAG_DISK_INIT = "disk_init";
private static final int METRICS_CATEGORY = SettingsEnums.DEVICEINFO_STORAGE;
private StorageManager mStorageManager;
private PreferenceCategory mInternalCategory;
private PreferenceCategory mExternalCategory;
private StorageSummaryPreference mInternalSummary;
private static long sTotalInternalStorage;
private boolean mHasLaunchedPrivateVolumeSettings = false;
@Override
public int getMetricsCategory() {
return METRICS_CATEGORY;
}
@Override
public int getHelpResource() {
return R.string.help_uri_storage;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getActivity();
mStorageManager = context.getSystemService(StorageManager.class);
if (sTotalInternalStorage <= 0) {
sTotalInternalStorage = mStorageManager.getPrimaryStorageSize();
}
addPreferencesFromResource(R.xml.device_info_storage);
mInternalCategory = (PreferenceCategory) findPreference("storage_internal");
mExternalCategory = (PreferenceCategory) findPreference("storage_external");
mInternalSummary = new StorageSummaryPreference(getPrefContext());
setHasOptionsMenu(true);
}
private final StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
if (isInteresting(vol)) {
refresh();
}
}
@Override
public void onDiskDestroyed(DiskInfo disk) {
refresh();
}
};
private static boolean isInteresting(VolumeInfo vol) {
switch (vol.getType()) {
case VolumeInfo.TYPE_PRIVATE:
case VolumeInfo.TYPE_PUBLIC:
case VolumeInfo.TYPE_STUB:
return true;
default:
return false;
}
}
private synchronized void refresh() {
final Context context = getPrefContext();
getPreferenceScreen().removeAll();
mInternalCategory.removeAll();
mExternalCategory.removeAll();
mInternalCategory.addPreference(mInternalSummary);
final StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(mStorageManager);
final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(smvp);
final long privateTotalBytes = info.totalBytes;
final long privateUsedBytes = info.totalBytes - info.freeBytes;
final List<VolumeInfo> volumes = mStorageManager.getVolumes();
Collections.sort(volumes, VolumeInfo.getDescriptionComparator());
for (VolumeInfo vol : volumes) {
if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
if (vol.getState() == VolumeInfo.STATE_UNMOUNTABLE) {
mInternalCategory.addPreference(
new StorageVolumePreference(context, vol, 0));
} else {
final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol,
sTotalInternalStorage);
mInternalCategory.addPreference(
new StorageVolumePreference(context, vol, volumeTotalBytes));
}
} else if (vol.getType() == VolumeInfo.TYPE_PUBLIC
|| vol.getType() == VolumeInfo.TYPE_STUB) {
mExternalCategory.addPreference(
new StorageVolumePreference(context, vol, 0));
}
}
// Show missing private volumes
final List<VolumeRecord> recs = mStorageManager.getVolumeRecords();
for (VolumeRecord rec : recs) {
if (rec.getType() == VolumeInfo.TYPE_PRIVATE
&& mStorageManager.findVolumeByUuid(rec.getFsUuid()) == null) {
// TODO: add actual storage type to record
final Preference pref = new Preference(context);
pref.setKey(rec.getFsUuid());
pref.setTitle(rec.getNickname());
pref.setSummary(com.android.internal.R.string.ext_media_status_missing);
pref.setIcon(R.drawable.ic_sim_sd);
mInternalCategory.addPreference(pref);
}
}
// Show unsupported disks to give a chance to init
final List<DiskInfo> disks = mStorageManager.getDisks();
for (DiskInfo disk : disks) {
if (disk.volumeCount == 0 && disk.size > 0) {
final Preference pref = new Preference(context);
pref.setKey(disk.getId());
pref.setTitle(disk.getDescription());
pref.setSummary(com.android.internal.R.string.ext_media_status_unsupported);
pref.setIcon(R.drawable.ic_sim_sd);
mExternalCategory.addPreference(pref);
}
}
final BytesResult result = Formatter.formatBytes(getResources(), privateUsedBytes, 0);
mInternalSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
result.value, result.units));
mInternalSummary.setSummary(getString(R.string.storage_volume_used_total,
Formatter.formatFileSize(context, privateTotalBytes)));
if (mInternalCategory.getPreferenceCount() > 0) {
getPreferenceScreen().addPreference(mInternalCategory);
}
if (mExternalCategory.getPreferenceCount() > 0) {
getPreferenceScreen().addPreference(mExternalCategory);
}
if (mInternalCategory.getPreferenceCount() == 2
&& mExternalCategory.getPreferenceCount() == 0) {
// Only showing primary internal storage, so just shortcut
if (!mHasLaunchedPrivateVolumeSettings) {
mHasLaunchedPrivateVolumeSettings = true;
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL);
new SubSettingLauncher(getActivity())
.setDestination(StorageDashboardFragment.class.getName())
.setArguments(args)
.setTitleRes(R.string.storage_settings)
.setSourceMetricsCategory(getMetricsCategory())
.launch();
finish();
}
}
}
@Override
public void onResume() {
super.onResume();
mStorageManager.registerListener(mStorageListener);
refresh();
}
@Override
public void onPause() {
super.onPause();
mStorageManager.unregisterListener(mStorageListener);
}
@Override
public boolean onPreferenceTreeClick(Preference pref) {
final String key = pref.getKey();
if (pref instanceof StorageVolumePreference) {
// Picked a normal volume
final VolumeInfo vol = mStorageManager.findVolumeById(key);
if (vol == null) {
return false;
}
if (vol.getState() == VolumeInfo.STATE_UNMOUNTED) {
VolumeUnmountedFragment.show(this, vol.getId());
return true;
} else if (vol.getState() == VolumeInfo.STATE_UNMOUNTABLE) {
DiskInitFragment.show(this, R.string.storage_dialog_unmountable, vol.getDiskId());
return true;
}
if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
new SubSettingLauncher(getContext())
.setDestination(StorageDashboardFragment.class.getCanonicalName())
.setTitleRes(R.string.storage_settings)
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args)
.launch();
} else {
// TODO: Go to the StorageDashboardFragment once it fully handles all of the
// SD card cases and other private internal storage cases.
PrivateVolumeSettings.setVolumeSize(args, PrivateStorageInfo.getTotalSize(vol,
sTotalInternalStorage));
new SubSettingLauncher(getContext())
.setDestination(PrivateVolumeSettings.class.getCanonicalName())
.setTitleRes(-1)
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args)
.launch();
}
return true;
} else if (vol.getType() == VolumeInfo.TYPE_PUBLIC) {
return handlePublicVolumeClick(getContext(), vol);
} else if (vol.getType() == VolumeInfo.TYPE_STUB) {
return handleStubVolumeClick(getContext(), vol);
}
} else if (key.startsWith("disk:")) {
// Picked an unsupported disk
DiskInitFragment.show(this, R.string.storage_dialog_unsupported, key);
return true;
} else {
// Picked a missing private volume
final Bundle args = new Bundle();
args.putString(VolumeRecord.EXTRA_FS_UUID, key);
new SubSettingLauncher(getContext())
.setDestination(PrivateVolumeForget.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_forget)
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args)
.launch();
return true;
}
return false;
}
@VisibleForTesting
static boolean handleStubVolumeClick(Context context, VolumeInfo vol) {
final Intent intent = vol.buildBrowseIntent();
if (vol.isMountedReadable() && intent != null) {
context.startActivity(intent);
return true;
}
return false;
}
@VisibleForTesting
static boolean handlePublicVolumeClick(Context context, VolumeInfo vol) {
final Intent intent = vol.buildBrowseIntent();
if (vol.isMountedReadable() && intent != null) {
context.startActivity(intent);
return true;
} else {
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
new SubSettingLauncher(context)
.setDestination(PublicVolumeSettings.class.getCanonicalName())
.setTitleRes(-1)
.setSourceMetricsCategory(METRICS_CATEGORY)
.setArguments(args)
.launch();
return true;
}
}
public static class MountTask extends AsyncTask<Void, Void, Exception> {
private final Context mContext;
private final StorageManager mStorageManager;
private final String mVolumeId;
private final String mDescription;
public MountTask(Context context, VolumeInfo volume) {
mContext = context.getApplicationContext();
mStorageManager = mContext.getSystemService(StorageManager.class);
mVolumeId = volume.getId();
mDescription = mStorageManager.getBestVolumeDescription(volume);
}
@Override
protected Exception doInBackground(Void... params) {
try {
mStorageManager.mount(mVolumeId);
return null;
} catch (Exception e) {
return e;
}
}
@Override
protected void onPostExecute(Exception e) {
if (e == null) {
Toast.makeText(mContext, mContext.getString(R.string.storage_mount_success,
mDescription), Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG, "Failed to mount " + mVolumeId, e);
Toast.makeText(mContext, mContext.getString(R.string.storage_mount_failure,
mDescription), Toast.LENGTH_SHORT).show();
}
}
}
public static class UnmountTask extends AsyncTask<Void, Void, Exception> {
private final Context mContext;
private final StorageManager mStorageManager;
private final String mVolumeId;
private final String mDescription;
public UnmountTask(Context context, VolumeInfo volume) {
mContext = context.getApplicationContext();
mStorageManager = mContext.getSystemService(StorageManager.class);
mVolumeId = volume.getId();
mDescription = mStorageManager.getBestVolumeDescription(volume);
}
@Override
protected Exception doInBackground(Void... params) {
try {
mStorageManager.unmount(mVolumeId);
return null;
} catch (Exception e) {
return e;
}
}
@Override
protected void onPostExecute(Exception e) {
if (e == null) {
Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success,
mDescription), Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG, "Failed to unmount " + mVolumeId, e);
Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure,
mDescription), Toast.LENGTH_SHORT).show();
}
}
}
public static class VolumeUnmountedFragment extends InstrumentedDialogFragment {
public static void show(Fragment parent, String volumeId) {
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeId);
final VolumeUnmountedFragment dialog = new VolumeUnmountedFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_VOLUME_UNMOUNTED);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_VOLUME_UNMOUNT;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final StorageManager sm = context.getSystemService(StorageManager.class);
final String volumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
final VolumeInfo vol = sm.findVolumeById(volumeId);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(TextUtils.expandTemplate(
getText(R.string.storage_dialog_unmounted), vol.getDisk().getDescription()));
builder.setPositiveButton(R.string.storage_menu_mount,
new DialogInterface.OnClickListener() {
/**
* Check if an {@link
* RestrictedLockUtils#sendShowAdminSupportDetailsIntent admin
* details intent} should be shown for the restriction and show it.
*
* @param restriction The restriction to check
* @return {@code true} iff a intent was shown.
*/
private boolean wasAdminSupportIntentShown(@NonNull String restriction) {
EnforcedAdmin admin = RestrictedLockUtilsInternal
.checkIfRestrictionEnforced(getActivity(), restriction,
UserHandle.myUserId());
boolean hasBaseUserRestriction =
RestrictedLockUtilsInternal.hasBaseUserRestriction(
getActivity(), restriction, UserHandle.myUserId());
if (admin != null && !hasBaseUserRestriction) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
admin);
return true;
}
return false;
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (wasAdminSupportIntentShown(
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
return;
}
if (vol.disk != null && vol.disk.isUsb() &&
wasAdminSupportIntentShown(
UserManager.DISALLOW_USB_FILE_TRANSFER)) {
return;
}
new MountTask(context, vol).execute();
}
});
builder.setNegativeButton(R.string.cancel, null);
return builder.create();
}
}
public static class DiskInitFragment extends InstrumentedDialogFragment {
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_VOLUME_INIT;
}
public static void show(Fragment parent, int resId, String diskId) {
final Bundle args = new Bundle();
args.putInt(Intent.EXTRA_TEXT, resId);
args.putString(DiskInfo.EXTRA_DISK_ID, diskId);
final DiskInitFragment dialog = new DiskInitFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_DISK_INIT);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final StorageManager sm = context.getSystemService(StorageManager.class);
final int resId = getArguments().getInt(Intent.EXTRA_TEXT);
final String diskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID);
final DiskInfo disk = sm.findDiskById(diskId);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(TextUtils.expandTemplate(getText(resId), disk.getDescription()));
builder.setPositiveButton(R.string.storage_menu_set_up,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final Intent intent = new Intent(context, StorageWizardInit.class);
intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
startActivity(intent);
}
});
builder.setNegativeButton(R.string.cancel, null);
return builder.create();
}
}
/** Enable indexing of searchable data */
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableRaw> getRawDataToIndex(
Context context, boolean enabled) {
final List<SearchIndexableRaw> result = new ArrayList<>();
SearchIndexableRaw data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.storage_settings);
data.key = KEY_STORAGE_SETTINGS;
data.screenTitle = context.getString(R.string.storage_settings);
data.keywords = context.getString(R.string.keywords_storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.internal_storage);
data.key = KEY_INTERNAL_STORAGE;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
final StorageManager storage = context.getSystemService(StorageManager.class);
final List<VolumeInfo> vols = storage.getVolumes();
for (VolumeInfo vol : vols) {
if (isInteresting(vol)) {
data.title = storage.getBestVolumeDescription(vol);
data.key = KEY_STORAGE_SETTINGS_VOLUME + vol.id;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
}
}
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.memory_size);
data.key = KEY_STORAGE_SETTINGS_MEMORY_SIZE;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.memory_available);
data.key = KEY_STORAGE_SETTINGS_MEMORY;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.memory_dcim_usage);
data.key = KEY_STORAGE_SETTINGS_DCIM;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.memory_music_usage);
data.key = KEY_STORAGE_SETTINGS_MUSIC;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.memory_media_misc_usage);
data.key = KEY_STORAGE_SETTINGS_MISC;
data.screenTitle = context.getString(R.string.storage_settings);
result.add(data);
data = new SearchIndexableRaw(context);
data.title = context.getString(R.string.storage_menu_free);
data.key = KEY_STORAGE_SETTINGS_FREE_SPACE;
data.screenTitle = context.getString(R.string.storage_menu_free);
data.intentAction = StorageManager.ACTION_MANAGE_STORAGE;
data.keywords = context.getString(R.string.keywords_storage_menu_free);
result.add(data);
return result;
}
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> niks = super.getNonIndexableKeys(context);
if (isExternalExist(context)) {
niks.add(KEY_STORAGE_SETTINGS);
niks.add(KEY_INTERNAL_STORAGE);
niks.add(KEY_STORAGE_SETTINGS_MEMORY_SIZE);
niks.add(KEY_STORAGE_SETTINGS_MEMORY);
niks.add(KEY_STORAGE_SETTINGS_DCIM);
niks.add(KEY_STORAGE_SETTINGS_MUSIC);
niks.add(KEY_STORAGE_SETTINGS_MISC);
niks.add(KEY_STORAGE_SETTINGS_FREE_SPACE);
final StorageManager storage = context.getSystemService(
StorageManager.class);
final List<VolumeInfo> vols = storage.getVolumes();
for (VolumeInfo vol : vols) {
if (isInteresting(vol)) {
niks.add(KEY_STORAGE_SETTINGS_VOLUME + vol.id);
}
}
}
return niks;
}
@Override
protected boolean isPageSearchEnabled(Context context) {
return !isExternalExist(context);
}
private boolean isExternalExist(Context context) {
int internalCount = 0;
StorageManager storageManager = context.getSystemService(StorageManager.class);
final List<VolumeInfo> volumes = storageManager.getVolumes();
for (VolumeInfo vol : volumes) {
//External storage
if (vol.getType() == VolumeInfo.TYPE_PUBLIC
|| vol.getType() == VolumeInfo.TYPE_STUB) {
return true;
} else if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
internalCount++;
}
}
// Unsupported disks
final List<DiskInfo> disks = storageManager.getDisks();
for (DiskInfo disk : disks) {
if (disk.volumeCount == 0 && disk.size > 0) {
return true;
}
}
// Missing private volumes
final List<VolumeRecord> recs = storageManager.getVolumeRecords();
for (VolumeRecord rec : recs) {
if (rec.getType() == VolumeInfo.TYPE_PRIVATE
&& storageManager.findVolumeByUuid(rec.getFsUuid()) == null) {
internalCount++;
}
}
return (internalCount != 1);
}
};
}

View File

@@ -16,8 +16,6 @@
package com.android.settings.deviceinfo; package com.android.settings.deviceinfo;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -25,9 +23,11 @@ import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo; import android.os.storage.VolumeInfo;
import android.util.Log; import android.util.Log;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask; import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
public class StorageUnmountReceiver extends BroadcastReceiver { public class StorageUnmountReceiver extends BroadcastReceiver {
private static final String TAG = "StorageUnmountReceiver";
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
final StorageManager storage = context.getSystemService(StorageManager.class); final StorageManager storage = context.getSystemService(StorageManager.class);

View File

@@ -1,153 +0,0 @@
/*
* Copyright (C) 2015 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.deviceinfo;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
import com.android.settingslib.Utils;
import java.io.File;
import java.io.IOException;
/**
* Preference line representing a single {@link VolumeInfo}, possibly including
* quick actions like unmounting.
*/
public class StorageVolumePreference extends Preference {
private static final String TAG = StorageVolumePreference.class.getSimpleName();
private final StorageManager mStorageManager;
private final VolumeInfo mVolume;
private int mUsedPercent = -1;
private ColorStateList mColorTintList;
// TODO: ideally, VolumeInfo should have a total physical size.
public StorageVolumePreference(Context context, VolumeInfo volume, long totalBytes) {
super(context);
mStorageManager = context.getSystemService(StorageManager.class);
mVolume = volume;
mColorTintList = Utils.getColorAttr(context, android.R.attr.colorControlNormal);
setLayoutResource(R.layout.storage_volume);
setKey(volume.getId());
setTitle(mStorageManager.getBestVolumeDescription(volume));
Drawable icon;
if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(volume.getId())) {
icon = context.getDrawable(R.drawable.ic_storage);
} else {
icon = context.getDrawable(R.drawable.ic_sim_sd);
}
if (volume.isMountedReadable()) {
// TODO: move statfs() to background thread
final File path = volume.getPath();
long freeBytes = 0;
long usedBytes = 0;
if (volume.getType() == VolumeInfo.TYPE_PRIVATE) {
final StorageStatsManager stats =
context.getSystemService(StorageStatsManager.class);
try {
totalBytes = stats.getTotalBytes(volume.getFsUuid());
freeBytes = stats.getFreeBytes(volume.getFsUuid());
usedBytes = totalBytes - freeBytes;
} catch (IOException e) {
Log.w(TAG, e);
}
} else {
// StorageStatsManager can only query private volumes.
// Default to previous storage calculation for public volumes.
if (totalBytes <= 0) {
totalBytes = path.getTotalSpace();
}
freeBytes = path.getFreeSpace();
usedBytes = totalBytes - freeBytes;
}
final String used = Formatter.formatFileSize(context, usedBytes);
final String total = Formatter.formatFileSize(context, totalBytes);
setSummary(context.getString(R.string.storage_volume_summary, used, total));
if (totalBytes > 0) {
mUsedPercent = (int) ((usedBytes * 100) / totalBytes);
}
if (freeBytes < mStorageManager.getStorageLowBytes(path)) {
mColorTintList = Utils.getColorAttr(context, android.R.attr.colorError);
icon = context.getDrawable(R.drawable.ic_warning_24dp);
icon.mutate();
icon.setTintList(mColorTintList);
}
} else {
setSummary(volume.getStateDescription());
mUsedPercent = -1;
}
setIcon(icon);
if (volume.getType() == VolumeInfo.TYPE_PUBLIC
&& volume.isMountedReadable()) {
setWidgetLayoutResource(R.layout.preference_storage_action);
}
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
final ImageView unmount = (ImageView) view.findViewById(R.id.unmount);
if (unmount != null) {
unmount.setOnClickListener(mUnmountListener);
}
final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE && mUsedPercent != -1) {
progress.setVisibility(View.VISIBLE);
progress.setProgress(mUsedPercent);
progress.setProgressTintList(mColorTintList);
} else {
progress.setVisibility(View.GONE);
}
super.onBindViewHolder(view);
}
private final View.OnClickListener mUnmountListener = new OnClickListener() {
@Override
public void onClick(View v) {
new UnmountTask(getContext(), mVolume).execute();
}
};
}

View File

@@ -19,8 +19,6 @@ package com.android.settings.deviceinfo;
import static android.os.storage.DiskInfo.EXTRA_DISK_ID; import static android.os.storage.DiskInfo.EXTRA_DISK_ID;
import static android.os.storage.VolumeInfo.EXTRA_VOLUME_ID; import static android.os.storage.VolumeInfo.EXTRA_VOLUME_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.annotation.LayoutRes; import android.annotation.LayoutRes;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.content.Intent; import android.content.Intent;
@@ -54,6 +52,8 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
public abstract class StorageWizardBase extends FragmentActivity { public abstract class StorageWizardBase extends FragmentActivity {
private static final String TAG = "StorageWizardBase";
protected static final String EXTRA_FORMAT_FORGET_UUID = "format_forget_uuid"; protected static final String EXTRA_FORMAT_FORGET_UUID = "format_forget_uuid";
protected static final String EXTRA_FORMAT_PRIVATE = "format_private"; protected static final String EXTRA_FORMAT_PRIVATE = "format_private";
protected static final String EXTRA_FORMAT_SLOW = "format_slow"; protected static final String EXTRA_FORMAT_SLOW = "format_slow";

View File

@@ -18,8 +18,6 @@ package com.android.settings.deviceinfo;
import static android.os.storage.VolumeInfo.TYPE_PRIVATE; import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.Intent; import android.content.Intent;
import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageMoveObserver;
import android.os.AsyncTask; import android.os.AsyncTask;
@@ -40,6 +38,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class StorageWizardFormatProgress extends StorageWizardBase { public class StorageWizardFormatProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardFormatProgress";
private static final String PROP_DEBUG_STORAGE_SLOW = "sys.debug.storage_slow"; private static final String PROP_DEBUG_STORAGE_SLOW = "sys.debug.storage_slow";
private boolean mFormatPrivate; private boolean mFormatPrivate;

View File

@@ -16,8 +16,6 @@
package com.android.settings.deviceinfo; package com.android.settings.deviceinfo;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -39,6 +37,8 @@ import com.android.settings.password.ChooseLockSettingsHelper;
import java.util.Objects; import java.util.Objects;
public class StorageWizardMigrateConfirm extends StorageWizardBase { public class StorageWizardMigrateConfirm extends StorageWizardBase {
private static final String TAG = "StorageWizardMigrateConfirm";
private static final int REQUEST_CREDENTIAL = 100; private static final int REQUEST_CREDENTIAL = 100;
private MigrateEstimateTask mEstimate; private MigrateEstimateTask mEstimate;

View File

@@ -18,8 +18,6 @@ package com.android.settings.deviceinfo;
import static android.content.pm.PackageManager.EXTRA_MOVE_ID; import static android.content.pm.PackageManager.EXTRA_MOVE_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -34,6 +32,8 @@ import android.widget.Toast;
import com.android.settings.R; import com.android.settings.R;
public class StorageWizardMigrateProgress extends StorageWizardBase { public class StorageWizardMigrateProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardMigrateProgress";
private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD"; private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
private int mMoveId; private int mMoveId;

View File

@@ -21,8 +21,6 @@ import static android.content.Intent.EXTRA_TITLE;
import static android.content.pm.PackageManager.EXTRA_MOVE_ID; import static android.content.pm.PackageManager.EXTRA_MOVE_ID;
import static android.os.storage.VolumeInfo.EXTRA_VOLUME_ID; import static android.os.storage.VolumeInfo.EXTRA_VOLUME_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +37,8 @@ import com.android.settings.R;
import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.password.ChooseLockSettingsHelper;
public class StorageWizardMoveConfirm extends StorageWizardBase { public class StorageWizardMoveConfirm extends StorageWizardBase {
private static final String TAG = "StorageWizardMoveConfirm";
private static final int REQUEST_CREDENTIAL = 100; private static final int REQUEST_CREDENTIAL = 100;
private String mPackageName; private String mPackageName;

View File

@@ -19,8 +19,6 @@ package com.android.settings.deviceinfo;
import static android.content.Intent.EXTRA_TITLE; import static android.content.Intent.EXTRA_TITLE;
import static android.content.pm.PackageManager.EXTRA_MOVE_ID; import static android.content.pm.PackageManager.EXTRA_MOVE_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.MoveCallback; import android.content.pm.PackageManager.MoveCallback;
import android.os.Bundle; import android.os.Bundle;
@@ -32,6 +30,8 @@ import android.widget.Toast;
import com.android.settings.R; import com.android.settings.R;
public class StorageWizardMoveProgress extends StorageWizardBase { public class StorageWizardMoveProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardMoveProgress";
private int mMoveId; private int mMoveId;
@Override @Override

View File

@@ -35,11 +35,11 @@ import androidx.fragment.app.Fragment;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.deviceinfo.StorageSettings.MountTask;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
import com.android.settings.deviceinfo.storage.StorageEntry; import com.android.settings.deviceinfo.storage.StorageEntry;
import com.android.settings.deviceinfo.storage.StorageRenameFragment; import com.android.settings.deviceinfo.storage.StorageRenameFragment;
import com.android.settings.deviceinfo.storage.StorageUtils; import com.android.settings.deviceinfo.storage.StorageUtils;
import com.android.settings.deviceinfo.storage.StorageUtils.MountTask;
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu; import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected; import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;

View File

@@ -45,8 +45,8 @@ import com.android.settings.Utils;
import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.deviceinfo.PrivateVolumeSettings.SystemInfoFragment;
import com.android.settings.deviceinfo.StorageItemPreference; import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settings.deviceinfo.storage.StorageUtils.SystemInfoFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;

View File

@@ -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.deviceinfo.storage;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.storage.StorageManager;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.DonutView;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
* StorageSummaryDonutPreference is a preference which summarizes the used and remaining storage left
* on a given storage volume. It is visualized with a donut graphing the % used.
*/
public class StorageSummaryDonutPreference extends Preference implements View.OnClickListener {
private double mPercent = -1;
public StorageSummaryDonutPreference(Context context) {
this(context, null);
}
public StorageSummaryDonutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.storage_summary_donut);
setEnabled(false);
}
public void setPercent(long usedBytes, long totalBytes) {
if (totalBytes == 0) {
return;
}
mPercent = usedBytes / (double) totalBytes;
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
view.itemView.setClickable(false);
final DonutView donut = (DonutView) view.findViewById(R.id.donut);
if (donut != null) {
donut.setPercentage(mPercent);
}
final Button deletionHelperButton = (Button) view.findViewById(R.id.deletion_helper_button);
if (deletionHelperButton != null) {
deletionHelperButton.setOnClickListener(this);
}
}
@Override
public void onClick(View v) {
if (v != null && R.id.deletion_helper_button == v.getId()) {
final Context context = getContext();
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
metricsFeatureProvider.logClickedPreference(this,
getExtras().getInt(DashboardFragment.CATEGORY));
metricsFeatureProvider.action(context, SettingsEnums.STORAGE_FREE_UP_SPACE_NOW);
final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
context.startActivity(intent);
}
}
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright (C) 2017 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.deviceinfo.storage;
import android.content.Context;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.text.format.Formatter;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import com.android.settingslib.utils.ThreadUtils;
import java.text.NumberFormat;
/**
* SummaryPreferenceController updates the donut storage summary preference to have the
* correct sizes showing.
*/
public class StorageSummaryDonutPreferenceController extends BasePreferenceController {
private long mUsedBytes;
private long mTotalBytes;
private StorageSummaryDonutPreference mSummary;
private final StorageManager mStorageManager;
private final StorageManagerVolumeProvider mStorageManagerVolumeProvider;
public StorageSummaryDonutPreferenceController(Context context, String key) {
super(context, key);
mStorageManager = mContext.getSystemService(StorageManager.class);
mStorageManagerVolumeProvider = new StorageManagerVolumeProvider(mStorageManager);
}
/**
* Converts a used storage amount to a formatted text.
*
* @param usedBytes used bytes of storage
* @return a formatted text.
*/
public static CharSequence convertUsedBytesToFormattedText(Context context, long usedBytes) {
final Formatter.BytesResult result = Formatter.formatBytes(context.getResources(),
usedBytes, 0);
return TextUtils.expandTemplate(context.getText(R.string.storage_size_large_alternate),
result.value, result.units);
}
@Override
public void displayPreference(PreferenceScreen screen) {
mSummary = screen.findPreference(getPreferenceKey());
mSummary.setEnabled(true);
ThreadUtils.postOnBackgroundThread(() -> {
final NumberFormat percentageFormat = NumberFormat.getPercentInstance();
final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
mStorageManagerVolumeProvider);
final double privateUsedBytes = info.totalBytes - info.freeBytes;
mTotalBytes = info.totalBytes;
mUsedBytes = info.totalBytes - info.freeBytes;
ThreadUtils.postOnMainThread(() -> {
updateState(mSummary);
});
});
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mSummary.setTitle(convertUsedBytesToFormattedText(mContext, mUsedBytes));
mSummary.setSummary(mContext.getString(R.string.storage_volume_total,
Formatter.formatShortFileSize(mContext, mTotalBytes)));
mSummary.setPercent(mUsedBytes, mTotalBytes);
mSummary.setEnabled(true);
}
/** Invalidates the data on the view and re-renders. */
public void invalidateData() {
if (mSummary != null) {
updateState(mSummary);
}
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
/**
* Updates the state of the donut preference for the next update.
*
* @param used Total number of used bytes on the summarized volume.
* @param total Total number of bytes on the summarized volume.
*/
public void updateBytes(long used, long total) {
mUsedBytes = used;
mTotalBytes = total;
invalidateData();
}
/**
* Updates the state of the donut preference for the next update using volume to summarize.
*
* @param volume VolumeInfo to use to populate the informayion.
*/
public void updateSizes(StorageVolumeProvider svp, VolumeInfo volume) {
final long sharedDataSize = volume.getPath().getTotalSpace();
long totalSize = svp.getPrimaryStorageSize();
if (totalSize <= 0) {
totalSize = sharedDataSize;
}
final long usedBytes = totalSize - volume.getPath().getFreeSpace();
updateBytes(usedBytes, totalSize);
}
}

View File

@@ -16,18 +16,31 @@
package com.android.settings.deviceinfo.storage; package com.android.settings.deviceinfo.storage;
import android.app.Dialog;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord; import android.os.storage.VolumeRecord;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.deviceinfo.PrivateVolumeForget; import com.android.settings.deviceinfo.PrivateVolumeForget;
/** Storage utilities */ /** Storage utilities */
public class StorageUtils { public class StorageUtils {
private static final String TAG = "StorageUtils";
/** Launches the fragment to forget a specified missing volume record. */ /** Launches the fragment to forget a specified missing volume record. */
public static void launchForgetMissingVolumeRecordFragment(Context context, public static void launchForgetMissingVolumeRecordFragment(Context context,
StorageEntry storageEntry) { StorageEntry storageEntry) {
@@ -44,4 +57,104 @@ public class StorageUtils {
.setArguments(args) .setArguments(args)
.launch(); .launch();
} }
/** An AsyncTask to unmount a specified volume. */
public static class UnmountTask extends AsyncTask<Void, Void, Exception> {
private final Context mContext;
private final StorageManager mStorageManager;
private final String mVolumeId;
private final String mDescription;
public UnmountTask(Context context, VolumeInfo volume) {
mContext = context.getApplicationContext();
mStorageManager = mContext.getSystemService(StorageManager.class);
mVolumeId = volume.getId();
mDescription = mStorageManager.getBestVolumeDescription(volume);
}
@Override
protected Exception doInBackground(Void... params) {
try {
mStorageManager.unmount(mVolumeId);
return null;
} catch (Exception e) {
return e;
}
}
@Override
protected void onPostExecute(Exception e) {
if (e == null) {
Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success,
mDescription), Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG, "Failed to unmount " + mVolumeId, e);
Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure,
mDescription), Toast.LENGTH_SHORT).show();
}
}
}
/** An AsyncTask to mount a specified volume. */
public static class MountTask extends AsyncTask<Void, Void, Exception> {
private final Context mContext;
private final StorageManager mStorageManager;
private final String mVolumeId;
private final String mDescription;
public MountTask(Context context, VolumeInfo volume) {
mContext = context.getApplicationContext();
mStorageManager = mContext.getSystemService(StorageManager.class);
mVolumeId = volume.getId();
mDescription = mStorageManager.getBestVolumeDescription(volume);
}
@Override
protected Exception doInBackground(Void... params) {
try {
mStorageManager.mount(mVolumeId);
return null;
} catch (Exception e) {
return e;
}
}
@Override
protected void onPostExecute(Exception e) {
if (e == null) {
Toast.makeText(mContext, mContext.getString(R.string.storage_mount_success,
mDescription), Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG, "Failed to mount " + mVolumeId, e);
Toast.makeText(mContext, mContext.getString(R.string.storage_mount_failure,
mDescription), Toast.LENGTH_SHORT).show();
}
}
}
/* Shows information about system storage. */
public static class SystemInfoFragment extends InstrumentedDialogFragment {
/** Shows the fragment. */
public static void show(Fragment parent) {
if (!parent.isAdded()) return;
final SystemInfoFragment dialog = new SystemInfoFragment();
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), "systemInfo");
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_STORAGE_SYSTEM_INFO;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage(getContext().getString(R.string.storage_detail_dialog_system,
Build.VERSION.RELEASE_OR_CODENAME))
.setPositiveButton(android.R.string.ok, null)
.create();
}
}
} }

View File

@@ -1,265 +0,0 @@
/*
* Copyright (C) 2017 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.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.icu.text.DecimalFormatSymbols;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.ColorRes;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Utils;
import java.util.Locale;
/**
* DonutView represents a donut graph. It visualizes a certain percentage of fullness with a
* corresponding label with the fullness on the inside (i.e. "50%" inside of the donut).
*/
public class DonutView extends View {
private static final int TOP = -90;
// From manual testing, this is the longest we can go without visual errors.
private static final int LINE_CHARACTER_LIMIT = 10;
private float mStrokeWidth;
private double mPercent;
private Paint mBackgroundCircle;
private Paint mFilledArc;
private TextPaint mTextPaint;
private TextPaint mBigNumberPaint;
private String mPercentString;
private String mFullString;
private boolean mShowPercentString = true;
private int mMeterBackgroundColor;
private int mMeterConsumedColor;
public DonutView(Context context) {
super(context);
}
public DonutView(Context context, AttributeSet attrs) {
super(context, attrs);
mMeterBackgroundColor = context.getColor(R.color.meter_background_color);
mMeterConsumedColor = Utils.getColorStateListDefaultColor(mContext,
R.color.meter_consumed_color);
boolean applyColorAccent = true;
Resources resources = context.getResources();
mStrokeWidth = resources.getDimension(R.dimen.storage_donut_thickness);
if (attrs != null) {
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.DonutView);
mMeterBackgroundColor = styledAttrs.getColor(R.styleable.DonutView_meterBackgroundColor,
mMeterBackgroundColor);
mMeterConsumedColor = styledAttrs.getColor(R.styleable.DonutView_meterConsumedColor,
mMeterConsumedColor);
applyColorAccent = styledAttrs.getBoolean(R.styleable.DonutView_applyColorAccent,
true);
mShowPercentString = styledAttrs.getBoolean(R.styleable.DonutView_showPercentString,
true);
mStrokeWidth = styledAttrs.getDimensionPixelSize(R.styleable.DonutView_thickness,
(int) mStrokeWidth);
styledAttrs.recycle();
}
mBackgroundCircle = new Paint();
mBackgroundCircle.setAntiAlias(true);
mBackgroundCircle.setStrokeCap(Paint.Cap.BUTT);
mBackgroundCircle.setStyle(Paint.Style.STROKE);
mBackgroundCircle.setStrokeWidth(mStrokeWidth);
mBackgroundCircle.setColor(mMeterBackgroundColor);
mFilledArc = new Paint();
mFilledArc.setAntiAlias(true);
mFilledArc.setStrokeCap(Paint.Cap.BUTT);
mFilledArc.setStyle(Paint.Style.STROKE);
mFilledArc.setStrokeWidth(mStrokeWidth);
mFilledArc.setColor(mMeterConsumedColor);
if (applyColorAccent) {
final ColorFilter mAccentColorFilter =
new PorterDuffColorFilter(
Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent),
PorterDuff.Mode.SRC_IN);
mBackgroundCircle.setColorFilter(mAccentColorFilter);
mFilledArc.setColorFilter(mAccentColorFilter);
}
final Locale locale = resources.getConfiguration().locale;
final int layoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
final int bidiFlags = (layoutDirection == LAYOUT_DIRECTION_LTR)
? Paint.BIDI_LTR
: Paint.BIDI_RTL;
mTextPaint = new TextPaint();
mTextPaint.setColor(Utils.getColorAccentDefaultColor(getContext()));
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(
resources.getDimension(R.dimen.storage_donut_view_label_text_size));
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setBidiFlags(bidiFlags);
mBigNumberPaint = new TextPaint();
mBigNumberPaint.setColor(Utils.getColorAccentDefaultColor(getContext()));
mBigNumberPaint.setAntiAlias(true);
mBigNumberPaint.setTextSize(
resources.getDimension(R.dimen.storage_donut_view_percent_text_size));
mBigNumberPaint.setTypeface(Typeface.create(
context.getString(com.android.internal.R.string.config_headlineFontFamily),
Typeface.NORMAL));
mBigNumberPaint.setBidiFlags(bidiFlags);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawDonut(canvas);
if (mShowPercentString) {
drawInnerText(canvas);
}
}
private void drawDonut(Canvas canvas) {
canvas.drawArc(
0 + mStrokeWidth,
0 + mStrokeWidth,
getWidth() - mStrokeWidth,
getHeight() - mStrokeWidth,
TOP,
360,
false,
mBackgroundCircle);
canvas.drawArc(
0 + mStrokeWidth,
0 + mStrokeWidth,
getWidth() - mStrokeWidth,
getHeight() - mStrokeWidth,
TOP,
(360 * (float) mPercent),
false,
mFilledArc);
}
private void drawInnerText(Canvas canvas) {
final float centerX = getWidth() / 2;
final float centerY = getHeight() / 2;
final float totalHeight = getTextHeight(mTextPaint) + getTextHeight(mBigNumberPaint);
final float startY = centerY + totalHeight / 2;
// Support from Android P
final String localizedPercentSign = new DecimalFormatSymbols().getPercentString();
// The first line y-coordinates start at (total height - all TextPaint height) / 2
canvas.save();
final Spannable percentStringSpan =
getPercentageStringSpannable(getResources(), mPercentString, localizedPercentSign);
final StaticLayout percentStringLayout = new StaticLayout(percentStringSpan,
mBigNumberPaint, getWidth(), Layout.Alignment.ALIGN_CENTER, 1, 0, false);
canvas.translate(0, (getHeight() - totalHeight) / 2);
percentStringLayout.draw(canvas);
canvas.restore();
// The second line starts at the bottom + room for the descender.
canvas.drawText(mFullString, centerX, startY - mTextPaint.descent(), mTextPaint);
}
/**
* Set a percentage full to have the donut graph.
*/
public void setPercentage(double percent) {
mPercent = percent;
mPercentString = Utils.formatPercentage(mPercent);
mFullString = getContext().getString(R.string.storage_percent_full);
if (mFullString.length() > LINE_CHARACTER_LIMIT) {
mTextPaint.setTextSize(
getContext()
.getResources()
.getDimension(
R.dimen.storage_donut_view_shrunken_label_text_size));
}
setContentDescription(getContext().getString(
R.string.join_two_unrelated_items, mPercentString, mFullString));
invalidate();
}
@ColorRes
public int getMeterBackgroundColor() {
return mMeterBackgroundColor;
}
public void setMeterBackgroundColor(@ColorRes int meterBackgroundColor) {
mMeterBackgroundColor = meterBackgroundColor;
mBackgroundCircle.setColor(meterBackgroundColor);
invalidate();
}
@ColorRes
public int getMeterConsumedColor() {
return mMeterConsumedColor;
}
public void setMeterConsumedColor(@ColorRes int meterConsumedColor) {
mMeterConsumedColor = meterConsumedColor;
mFilledArc.setColor(meterConsumedColor);
invalidate();
}
@VisibleForTesting
static Spannable getPercentageStringSpannable(
Resources resources, String percentString, String percentageSignString) {
final float fontProportion =
resources.getDimension(R.dimen.storage_donut_view_percent_sign_size)
/ resources.getDimension(R.dimen.storage_donut_view_percent_text_size);
final Spannable percentStringSpan = new SpannableString(percentString);
int startIndex = percentString.indexOf(percentageSignString);
int endIndex = startIndex + percentageSignString.length();
// Fallback to no small string if we can't find the percentage sign.
if (startIndex < 0) {
startIndex = 0;
endIndex = percentString.length();
}
percentStringSpan.setSpan(
new RelativeSizeSpan(fontProportion),
startIndex,
endIndex,
Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
return percentStringSpan;
}
private float getTextHeight(TextPaint paint) {
// Technically, this should be the cap height, but I can live with the descent - ascent.
return paint.descent() - paint.ascent();
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright (C) 2017 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.deviceinfo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Intent;
import android.os.storage.VolumeInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class StorageSettingsTest {
@Mock
private StorageManagerVolumeProvider mStorageManagerVolumeProvider;
@Mock
private Activity mActivity;
private List<VolumeInfo> mVolumes;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mVolumes = new ArrayList<>();
mVolumes.add(mock(VolumeInfo.class, RETURNS_DEEP_STUBS));
when(mStorageManagerVolumeProvider.getVolumes()).thenReturn(mVolumes);
}
@Test
public void handlePublicVolumeClick_startsANonNullActivityWhenVolumeHasNoBrowse() {
VolumeInfo volumeInfo = mock(VolumeInfo.class, RETURNS_DEEP_STUBS);
when(volumeInfo.isMountedReadable()).thenReturn(true);
StorageSettings.handlePublicVolumeClick(mActivity, volumeInfo);
verify(mActivity, never()).startActivity(null);
verify(mActivity).startActivity(any(Intent.class));
}
@Test
public void handleStubVolumeClick_startsANonNullActivityWhenVolumeHasNoBrowse() {
VolumeInfo volumeInfo = mock(VolumeInfo.class, RETURNS_DEEP_STUBS);
when(volumeInfo.isMountedReadable()).thenReturn(true);
StorageSettings.handleStubVolumeClick(mActivity, volumeInfo);
verify(mActivity, never()).startActivity(null);
verify(mActivity).startActivity(any(Intent.class));
}
}

View File

@@ -48,7 +48,6 @@ import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings; import com.android.settings.SubSettings;
import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment; import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.PrivateVolumeSettings;
import com.android.settings.deviceinfo.StorageItemPreference; import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.applications.StorageStatsSource; import com.android.settingslib.applications.StorageStatsSource;
@@ -329,7 +328,7 @@ public class StorageItemPreferenceControllerTest {
assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue();
verify(mFragment.getFragmentManager().beginTransaction()) verify(mFragment.getFragmentManager().beginTransaction())
.add(nullable(PrivateVolumeSettings.SystemInfoFragment.class), nullable(String.class)); .add(nullable(StorageUtils.SystemInfoFragment.class), nullable(String.class));
} }
@Test @Test

View File

@@ -1,164 +0,0 @@
/*
* Copyright (C) 2017 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.deviceinfo.storage;
import static com.android.settings.TestUtils.GIGABYTE;
import static com.android.settings.TestUtils.KILOBYTE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.os.storage.VolumeInfo;
import android.text.format.Formatter;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowPrivateStorageInfo;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.File;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowPrivateStorageInfo.class)
public class StorageSummaryDonutPreferenceControllerTest {
private Context mContext;
private StorageSummaryDonutPreferenceController mController;
private StorageSummaryDonutPreference mPreference;
private PreferenceViewHolder mHolder;
private FakeFeatureFactory mFakeFeatureFactory;
private MetricsFeatureProvider mMetricsFeatureProvider;
private PreferenceScreen mScreen;
@Before
public void setUp() throws Exception {
ShadowPrivateStorageInfo.setPrivateStorageInfo(new PrivateStorageInfo(10L, 100L));
mContext = spy(Robolectric.setupActivity(Activity.class));
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
mController = new StorageSummaryDonutPreferenceController(mContext, "key");
mPreference = new StorageSummaryDonutPreference(mContext);
mScreen = spy(new PreferenceScreen(mContext, null));
when(mScreen.findPreference("key")).thenReturn(mPreference);
LayoutInflater inflater = LayoutInflater.from(mContext);
final View view =
inflater.inflate(mPreference.getLayoutResource(), new LinearLayout(mContext),
false);
mHolder = PreferenceViewHolder.createInstanceForTests(view);
}
@After
public void tearDown() {
ShadowPrivateStorageInfo.reset();
}
@Test
public void testEmpty() {
final long totalSpace = 32 * GIGABYTE;
final long usedSpace = 0;
mController.displayPreference(mScreen);
mController.updateBytes(0, 32 * GIGABYTE);
mController.updateState(mPreference);
final Formatter.BytesResult usedSpaceResults =
Formatter.formatBytes(mContext.getResources(), usedSpace, 0 /* flags */);
assertThat(mPreference.getTitle().toString())
.isEqualTo(usedSpaceResults.value + " " + usedSpaceResults.units);
assertThat(mPreference.getSummary().toString())
.isEqualTo("Used of " + Formatter.formatShortFileSize(mContext, totalSpace));
}
@Test
public void testTotalStorage() {
final long totalSpace = KILOBYTE * 10;
final long usedSpace = KILOBYTE;
mController.displayPreference(mScreen);
mController.updateBytes(KILOBYTE, totalSpace);
mController.updateState(mPreference);
final Formatter.BytesResult usedSpaceResults =
Formatter.formatBytes(mContext.getResources(), usedSpace, 0 /* flags */);
assertThat(mPreference.getTitle().toString())
.isEqualTo(usedSpaceResults.value + " " + usedSpaceResults.units);
assertThat(mPreference.getSummary().toString())
.isEqualTo("Used of " + Formatter.formatShortFileSize(mContext, totalSpace));
}
@Test
public void testPopulateWithVolume() {
final long totalSpace = KILOBYTE * 10;
final long freeSpace = KILOBYTE;
final long usedSpace = totalSpace - freeSpace;
final VolumeInfo volume = Mockito.mock(VolumeInfo.class);
final File file = Mockito.mock(File.class);
final StorageVolumeProvider svp = Mockito.mock(StorageVolumeProvider.class);
when(volume.getPath()).thenReturn(file);
when(file.getTotalSpace()).thenReturn(totalSpace);
when(file.getFreeSpace()).thenReturn(freeSpace);
when(svp.getPrimaryStorageSize()).thenReturn(totalSpace);
mController.displayPreference(mScreen);
mController.updateSizes(svp, volume);
mController.updateState(mPreference);
final Formatter.BytesResult usedSpaceResults =
Formatter.formatBytes(mContext.getResources(), usedSpace, 0 /* flags */);
assertThat(mPreference.getTitle().toString())
.isEqualTo(usedSpaceResults.value + " " + usedSpaceResults.units);
assertThat(mPreference.getSummary().toString())
.isEqualTo("Used of " + Formatter.formatShortFileSize(mContext, totalSpace));
}
@Test
public void testFreeUpSpaceMetricIsTriggered() {
mPreference.onBindViewHolder(mHolder);
final Button button = (Button) mHolder.findViewById(R.id.deletion_helper_button);
mPreference.onClick(button);
verify(mMetricsFeatureProvider, times(1))
.action(any(Context.class), eq(MetricsEvent.STORAGE_FREE_UP_SPACE_NOW));
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2020 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.widget;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class DonutViewTest {
@Test
public void getPercentageStringSpannable_doesntCrashForMissingPercentage() {
Context context = ApplicationProvider.getApplicationContext();
DonutView.getPercentageStringSpannable(context.getResources(), "50%", "h");
}
}