Snap for 7286185 from acb5bebb3d to sc-release

Change-Id: I063f14b65ac6598e9a86b22ca96325c47d19d713
This commit is contained in:
android-build-team Robot
2021-04-16 01:08:25 +00:00
59 changed files with 680 additions and 3026 deletions

View File

@@ -1296,7 +1296,7 @@
<activity
android:name=".notification.history.NotificationHistoryActivity"
android:exported="true"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:theme="@style/Theme.NotificationHistory"
android:label="@string/notification_history_title">
<intent-filter android:priority="1">
<action android:name="android.settings.NOTIFICATION_HISTORY" />
@@ -1979,13 +1979,6 @@
android:value="true" />
</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
android:name="Settings$PublicVolumeSettingsActivity"
android:exported="true"

View File

@@ -0,0 +1,63 @@
<!--
Copyright (C) 2021 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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid
android:color="@color/google_grey_700"/>
<size
android:height="@dimen/accessibility_icon_size"
android:width="@dimen/accessibility_icon_size"/>
</shape>
</item>
<item
android:gravity="center">
<vector
android:width="@dimen/accessibility_icon_foreground_size"
android:height="@dimen/accessibility_icon_foreground_size"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M17,12.1L15.59,10.69L13.05,13.22V7.05H11.05V13.22L8.51,10.69L7.1,12.1L12.05,17.05L17,12.1Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M2.05,13.05H4.05C4.6,13.05 5.05,12.6 5.05,12.05C5.05,11.5 4.6,11.05 4.05,11.05H2.05C1.5,11.05 1.05,11.5 1.05,12.05C1.05,12.6 1.5,13.05 2.05,13.05Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M20.05,13.05H22.05C22.6,13.05 23.05,12.6 23.05,12.05C23.05,11.5 22.6,11.05 22.05,11.05H20.05C19.5,11.05 19.05,11.5 19.05,12.05C19.05,12.6 19.5,13.05 20.05,13.05Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M11.05,2.05V4.05C11.05,4.6 11.5,5.05 12.05,5.05C12.6,5.05 13.05,4.6 13.05,4.05V2.05C13.05,1.5 12.6,1.05 12.05,1.05C11.5,1.05 11.05,1.5 11.05,2.05Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M11.05,20.05V22.05C11.05,22.6 11.5,23.05 12.05,23.05C12.6,23.05 13.05,22.6 13.05,22.05V20.05C13.05,19.5 12.6,19.05 12.05,19.05C11.5,19.05 11.05,19.5 11.05,20.05Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M6.04,4.63C5.65,4.24 5.01,4.24 4.63,4.63C4.24,5.02 4.24,5.66 4.63,6.04L5.69,7.1C6.08,7.49 6.72,7.49 7.1,7.1C7.49,6.71 7.49,6.07 7.1,5.69L6.04,4.63Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18.41,17C18.02,16.61 17.38,16.61 17,17C16.61,17.39 16.61,18.03 17,18.41L18.06,19.47C18.45,19.86 19.09,19.86 19.47,19.47C19.86,19.08 19.86,18.44 19.47,18.06L18.41,17Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M19.47,6.04C19.86,5.65 19.86,5.01 19.47,4.63C19.08,4.24 18.44,4.24 18.06,4.63L17,5.69C16.61,6.08 16.61,6.72 17,7.1C17.39,7.49 18.03,7.49 18.41,7.1L19.47,6.04Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M7.1,18.41C7.49,18.02 7.49,17.38 7.1,17C6.71,16.61 6.07,16.61 5.69,17L4.63,18.06C4.24,18.45 4.24,19.09 4.63,19.47C5.02,19.86 5.66,19.86 6.04,19.47L7.1,18.41Z"
android:fillColor="#ffffff"/>
</vector>
</item>
</layer-list>

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" />
<item
android:id="@+id/storage_format_as_portable"
android:title="@string/storage_menu_format_public"
android:visible="false" />
android:title="@string/storage_menu_format_public" />
<item
android:id="@+id/storage_format_as_internal"
android:title="@string/storage_menu_format_private"
android:visible="false" />
android:title="@string/storage_menu_format_private" />
<item
android:id="@+id/storage_migrate"
android:title="@string/storage_menu_migrate" />
@@ -43,6 +41,5 @@
android:title="@string/storage_menu_free" />
<item
android:id="@+id/storage_forget"
android:title="@string/storage_menu_forget"
android:visible="false" />
android:title="@string/storage_menu_forget" />
</menu>

View File

@@ -30,9 +30,4 @@
<!-- Display, Screen zoom -->
<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>

View File

@@ -25,8 +25,4 @@
<!-- Suggestion cards-->
<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>

View File

@@ -139,15 +139,6 @@
<attr name="android:gravity" />
</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 -->
<declare-styleable name="BiometricEnrollCheckbox">
<attr name="icon" format="reference" />

View File

@@ -189,4 +189,7 @@
<color name="SIM_dark_mode_color_purple">#ffe1bee7</color> <!-- Material Purple 100 -->
<color name="SIM_dark_mode_color_pink">#fff48fb1</color> <!-- Material Pink 200 -->
<color name="SIM_dark_mode_color_red">#ffef9a9a</color> <!-- Material Red 200 -->
<!-- Google colors -->
<color name="google_grey_700">#5f6368</color>
</resources>

View File

@@ -295,6 +295,10 @@
<!-- The margin between two Textviews-->
<dimen name="accessibility_textview_layout_margin_bottom">24dp</dimen>
<!-- Accessibility icon -->
<dimen name="accessibility_icon_size">32dp</dimen>
<dimen name="accessibility_icon_foreground_size">18dp</dimen>
<!-- Restricted icon in switch bar -->
<dimen name="restricted_icon_margin_end">16dp</dimen>
<!-- Restricted icon size in switch bar -->
@@ -321,19 +325,6 @@
<dimen name="select_dialog_item_margin_start">12dp</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 -->
<dimen name="battery_meter_width">66dp</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>
<!-- 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] -->
<string name="mtp_ptp_mode_summary">MTP or PTP function is active</string>
@@ -3482,8 +3477,6 @@
<string name="storage_menu_forget">Forget</string>
<!-- Storage setting. Menu option for setting up a storage device [CHAR LIMIT=30]-->
<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] -->
<string name="storage_menu_free">Free up space</string>
<!-- 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]-->
<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] -->
<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]-->
<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]-->
<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.
\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]-->
<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.
@@ -3582,34 +3567,11 @@
<!-- 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>
<!-- 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]-->
<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]-->
<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] -->
<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] -->
@@ -12604,6 +12566,8 @@
<string name="sim_action_restart_text">To get started, restart your device. Then you can add another SIM.</string>
<!-- Button on a dialog to confirm SIM operations. [CHAR LIMIT=30] -->
<string name="sim_action_continue">Continue</string>
<!-- Button on a dialog to confirm SIM operations. [CHAR LIMIT=30] -->
<string name="sim_action_yes">Yes</string>
<!-- User confirms reboot the phone. [CHAR LIMIT=30] -->
<string name="sim_action_reboot">Restart</string>
<!-- Button on a dialog to reject SIM operations. [CHAR LIMIT=30] -->

View File

@@ -281,4 +281,9 @@
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
<item name="colorAccent">@*android:color/accent_device_default_light</item>
</style>
<style name="Theme.NotificationHistory" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>

View File

@@ -43,9 +43,9 @@
android:title="@string/accessibility_text_and_display_title"
settings:searchable="true"/>
<!--TODO(b/170973645): Get icon-->
<com.android.settings.widget.PrimarySwitchPreference
android:fragment="com.android.settings.accessibility.ToggleReduceBrightColorsPreferenceFragment"
android:icon="@drawable/ic_reduce_bright_colors"
android:key="reduce_bright_colors_preference"
android:persistent="false"
android:title="@string/reduce_bright_colors_preference_title"

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
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">
android:title="@string/storage_settings">
</PreferenceScreen>

View File

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

View File

@@ -40,4 +40,9 @@
android:key="transcode_notification"
android:title="@string/transcode_notification"
settings:controller="com.android.settings.development.transcode.TranscodeNotificationPreferenceController" />
<SwitchPreference
android:key="transcode_disable_cache"
android:title="@string/transcode_disable_cache"
settings:controller="com.android.settings.development.transcode.TranscodeDisableCachePreferenceController" />
</PreferenceScreen>

View File

@@ -79,7 +79,6 @@ public class Settings extends SettingsActivity {
public static class DataSaverSummaryActivity extends SettingsActivity{ /* empty */ }
public static class DateTimeSettingsActivity 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 WifiSettingsActivity 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.development.DevelopmentSettingsDashboardFragment;
import com.android.settings.deviceinfo.PrivateVolumeForget;
import com.android.settings.deviceinfo.PrivateVolumeSettings;
import com.android.settings.deviceinfo.PublicVolumeSettings;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings;
import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard;
@@ -215,9 +213,7 @@ public class SettingsGateway {
ToggleDaltonizerPreferenceFragment.class.getName(),
ToggleReduceBrightColorsPreferenceFragment.class.getName(),
TextToSpeechSettings.class.getName(),
StorageSettings.class.getName(),
PrivateVolumeForget.class.getName(),
PrivateVolumeSettings.class.getName(),
PublicVolumeSettings.class.getName(),
DevelopmentSettingsDashboardFragment.class.getName(),
AndroidBeam.class.getName(),

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2021 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.development.transcode;
import android.content.Context;
import android.os.SystemProperties;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.core.TogglePreferenceController;
/**
* The controller (in the Media transcoding settings) indicating the user's preference to disable
* the cache for transcoding.
*/
public class TranscodeDisableCachePreferenceController extends TogglePreferenceController {
@VisibleForTesting
static final String TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY =
"persist.sys.fuse.disable_transcode_cache";
public TranscodeDisableCachePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public boolean isChecked() {
return SystemProperties.getBoolean(TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY, false);
}
@Override
public boolean setChecked(boolean isChecked) {
SystemProperties.set(TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY, String.valueOf(isChecked));
return true;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -16,8 +16,6 @@
package com.android.settings.deviceinfo;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.app.usage.ExternalStorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
@@ -37,6 +35,8 @@ import java.io.IOException;
import java.util.UUID;
public abstract class MigrateEstimateTask extends AsyncTask<Void, Void, Long> {
private static final String TAG = "MigrateEstimateTask";
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.core.InstrumentedFragment;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
import com.android.settings.search.actionbar.SearchMenuController;
public class PrivateVolumeUnmount extends InstrumentedFragment {

View File

@@ -40,8 +40,8 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.deviceinfo.StorageSettings.MountTask;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
import com.android.settings.deviceinfo.storage.StorageUtils.MountTask;
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
import java.io.File;
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;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -25,9 +23,11 @@ import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.Log;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
public class StorageUnmountReceiver extends BroadcastReceiver {
private static final String TAG = "StorageUnmountReceiver";
@Override
public void onReceive(Context context, Intent intent) {
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.VolumeInfo.EXTRA_VOLUME_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.content.Intent;
@@ -54,6 +52,8 @@ import java.util.List;
import java.util.Objects;
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_PRIVATE = "format_private";
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 com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.Intent;
import android.content.pm.IPackageMoveObserver;
import android.os.AsyncTask;
@@ -40,6 +38,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class StorageWizardFormatProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardFormatProgress";
private static final String PROP_DEBUG_STORAGE_SLOW = "sys.debug.storage_slow";
private boolean mFormatPrivate;

View File

@@ -16,8 +16,6 @@
package com.android.settings.deviceinfo;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -39,6 +37,8 @@ import com.android.settings.password.ChooseLockSettingsHelper;
import java.util.Objects;
public class StorageWizardMigrateConfirm extends StorageWizardBase {
private static final String TAG = "StorageWizardMigrateConfirm";
private static final int REQUEST_CREDENTIAL = 100;
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 com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -34,6 +32,8 @@ import android.widget.Toast;
import com.android.settings.R;
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 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.os.storage.VolumeInfo.EXTRA_VOLUME_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +37,8 @@ import com.android.settings.R;
import com.android.settings.password.ChooseLockSettingsHelper;
public class StorageWizardMoveConfirm extends StorageWizardBase {
private static final String TAG = "StorageWizardMoveConfirm";
private static final int REQUEST_CREDENTIAL = 100;
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.pm.PackageManager.EXTRA_MOVE_ID;
import static com.android.settings.deviceinfo.StorageSettings.TAG;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.MoveCallback;
import android.os.Bundle;
@@ -32,6 +30,8 @@ import android.widget.Toast;
import com.android.settings.R;
public class StorageWizardMoveProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardMoveProgress";
private int mMoveId;
@Override

View File

@@ -35,11 +35,11 @@ import androidx.fragment.app.Fragment;
import com.android.settings.R;
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.StorageRenameFragment;
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.events.OnCreateOptionsMenu;
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.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.deviceinfo.PrivateVolumeSettings.SystemInfoFragment;
import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settings.deviceinfo.storage.StorageUtils.SystemInfoFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
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;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
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.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.deviceinfo.PrivateVolumeForget;
/** Storage utilities */
public class StorageUtils {
private static final String TAG = "StorageUtils";
/** Launches the fragment to forget a specified missing volume record. */
public static void launchForgetMissingVolumeRecordFragment(Context context,
StorageEntry storageEntry) {
@@ -44,4 +57,104 @@ public class StorageUtils {
.setArguments(args)
.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

@@ -17,12 +17,18 @@
package com.android.settings.emergency;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.EmergencyBroadcastPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
/**
* {@link DashboardFragment} that hosts emergency/safety related settings.
@@ -31,6 +37,7 @@ import com.android.settingslib.search.SearchIndexable;
public class EmergencyDashboardFragment extends DashboardFragment {
private static final String TAG = "EmergencyDashboard";
private static final String WEA_PREF_KEY = "app_and_notif_cell_broadcast_settings";
@Override
protected int getPreferenceScreenResId() {
@@ -47,6 +54,17 @@ public class EmergencyDashboardFragment extends DashboardFragment {
return SettingsEnums.EMERGENCY_SETTINGS;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context);
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new EmergencyBroadcastPreferenceController(context, WEA_PREF_KEY));
return controllers;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.emergency_settings);
}

View File

@@ -255,7 +255,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private int getTrapezoidIndex(float x) {
for (int index = 0; index < mTrapezoidSlot.length; index++) {
final TrapezoidSlot slot = mTrapezoidSlot[index];
if (x >= slot.mLeft && x <= slot.mRight) {
if (x >= slot.mLeft - mTrapezoidHOffset
&& x <= slot.mRight + mTrapezoidHOffset) {
return index;
}
}

View File

@@ -22,13 +22,22 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/** A container class to carry battery data in a specific time slot. */
public final class BatteryDiffEntry {
private static final String TAG = "BatteryDiffEntry";
static Locale sCurrentLocale = null;
// Caches app label and icon to improve loading performance.
static final Map<String, BatteryEntry.NameAndIcon> sResourceCache = new HashMap<>();
/** A comparator for {@link BatteryDiffEntry} based on consumed percentage. */
public static final Comparator<BatteryDiffEntry> COMPARATOR =
(a, b) -> Double.compare(b.getPercentOfTotal(), a.getPercentOfTotal());
@@ -42,9 +51,11 @@ public final class BatteryDiffEntry {
private double mPercentOfTotal;
private Context mContext;
private String mAppLabel = null;
private Drawable mAppIcon = null;
private boolean mIsLoaded = false;
private String mDefaultPackageName = null;
@VisibleForTesting String mAppLabel = null;
@VisibleForTesting Drawable mAppIcon = null;
@VisibleForTesting boolean mIsLoaded = false;
public BatteryDiffEntry(
Context context,
@@ -84,7 +95,10 @@ public final class BatteryDiffEntry {
/** Gets the app label name for this entry. */
public String getAppLabel() {
loadLabelAndIcon();
return mAppLabel;
// Returns default applicationn label if we cannot find it.
return mAppLabel == null || mAppLabel.length() == 0
? mBatteryHistEntry.mAppLabel
: mAppLabel;
}
/** Gets the app icon {@link Drawable} for this entry. */
@@ -93,6 +107,11 @@ public final class BatteryDiffEntry {
return mAppIcon;
}
/** Gets the searching package name for UID battery type. */
public String getPackageName() {
return mDefaultPackageName;
}
private void loadLabelAndIcon() {
if (mIsLoaded) {
return;
@@ -121,13 +140,80 @@ public final class BatteryDiffEntry {
}
break;
case ConvertUtils.CONSUMER_TYPE_UID_BATTERY:
final BatteryEntry.NameAndIcon nameAndIcon = getCache();
if (nameAndIcon != null) {
mAppLabel = nameAndIcon.name;
mAppIcon = nameAndIcon.icon;
break;
}
loadNameAndIconForUid();
// Uses application default icon if we cannot find it from package.
if (mAppIcon == null) {
mAppIcon = mContext.getPackageManager().getDefaultActivityIcon();
}
if (mAppLabel != null && mAppIcon != null) {
sResourceCache.put(
mBatteryHistEntry.getKey(),
new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /*iconId=*/ 0));
}
break;
}
}
private BatteryEntry.NameAndIcon getCache() {
final Locale locale = Locale.getDefault();
if (sCurrentLocale != locale) {
Log.d(TAG, String.format("clearCache() locale is changed from %s to %s",
sCurrentLocale, locale));
sCurrentLocale = locale;
clearCache();
}
return sResourceCache.get(mBatteryHistEntry.getKey());
}
private void loadNameAndIconForUid() {
// TODO(b/185187669) fetch label and icon for UID battery type
final String packageName = mBatteryHistEntry.mPackageName;
final PackageManager packageManager = mContext.getPackageManager();
// Gets the application label from PackageManager.
if (packageName != null && packageName.length() != 0) {
try {
final ApplicationInfo appInfo =
packageManager.getApplicationInfo(packageName, /*no flags*/ 0);
if (appInfo != null) {
mAppLabel = packageManager.getApplicationLabel(appInfo).toString();
}
} catch (NameNotFoundException e) {
Log.e(TAG, "failed to retrieve ApplicationInfo for: " + packageName);
mAppLabel = packageName;
}
}
final int uid = (int) mBatteryHistEntry.mUid;
final String[] packages = packageManager.getPackagesForUid(uid);
// Loads special defined application label and icon if available.
if (packages == null || packages.length == 0) {
final BatteryEntry.NameAndIcon nameAndIcon =
BatteryEntry.getNameAndIconFromUid(mContext, mAppLabel, uid);
mAppLabel = nameAndIcon.name;
mAppIcon = nameAndIcon.icon;
}
final BatteryEntry.NameAndIcon nameAndIcon =
BatteryEntry.loadNameAndIcon(
mContext, uid, /*uid=*/ null, /*batteryEntry=*/ null,
packageName, mAppLabel, mAppIcon);
// Clears BatteryEntry internal cache since we will have another one.
BatteryEntry.clearUidCache();
if (nameAndIcon != null) {
mAppLabel = getNonNull(mAppLabel, nameAndIcon.name);
mAppIcon = getNonNull(mAppIcon, nameAndIcon.icon);
mDefaultPackageName = nameAndIcon.packageName;
if (mDefaultPackageName != null
&& !mDefaultPackageName.equals(nameAndIcon.packageName)) {
Log.w(TAG, String.format("found different package: %s | %s",
mDefaultPackageName, nameAndIcon.packageName));
}
}
}
@Override
@@ -144,4 +230,12 @@ public final class BatteryDiffEntry {
mBatteryHistEntry.mPackageName, mBatteryHistEntry.mUid));
return builder.toString();
}
static void clearCache() {
sResourceCache.clear();
}
private static <T> T getNonNull(T originalObj, T newObj) {
return newObj != null ? newObj : originalObj;
}
}

View File

@@ -110,7 +110,8 @@ public class BatteryEntry {
}
final NameAndIcon nameAndIcon =
BatteryEntry.loadNameAndIcon(
be.mContext, be.getUid(), sHandler, be, be.mDefaultPackageName);
be.mContext, be.getUid(), sHandler, be,
be.mDefaultPackageName, be.name, be.icon);
if (nameAndIcon != null) {
be.icon = getNonNull(be.icon, nameAndIcon.icon);
be.name = getNonNull(be.name, nameAndIcon.name);
@@ -273,6 +274,7 @@ public class BatteryEntry {
icon = mContext.getPackageManager().getDefaultActivityIcon();
}
// Avoids post the loading icon and label in the background request.
if (sHandler != null && loadDataInBackground) {
synchronized (sRequestQueue) {
sRequestQueue.add(this);
@@ -288,9 +290,9 @@ public class BatteryEntry {
int uid,
Handler handler,
BatteryEntry batteryEntry,
String defaultPackageName) {
String name = null;
Drawable icon = null;
String defaultPackageName,
String name,
Drawable icon) {
// Bail out if the current sipper is not an App sipper.
if (uid == 0 || uid == Process.INVALID_UID) {
return null;

View File

@@ -65,6 +65,7 @@ public final class BatteryHistEntry {
public final int mBatteryStatus;
public final int mBatteryHealth;
private String mKey = null;
private boolean mIsValidEntry = true;
public BatteryHistEntry(ContentValues values) {
@@ -114,7 +115,20 @@ public final class BatteryHistEntry {
/** Gets an identifier to represent this {@link BatteryHistEntry}. */
public String getKey() {
return mPackageName + "-" + mUserId;
if (mKey == null) {
switch (mConsumerType) {
case ConvertUtils.CONSUMER_TYPE_UID_BATTERY:
mKey = Long.toString(mUid);
break;
case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
mKey = "S|" + mDrainType;
break;
case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
mKey = "U|" + mUserId;
break;
}
}
return mKey;
}
@Override

View File

@@ -28,6 +28,7 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
@@ -48,7 +49,7 @@ public class ChangeScreenLockPreferenceController extends AbstractPreferenceCont
private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
protected final DevicePolicyManager mDPM;
protected final SecuritySettings mHost;
protected final SettingsPreferenceFragment mHost;
protected final UserManager mUm;
protected final LockPatternUtils mLockPatternUtils;
@@ -58,7 +59,7 @@ public class ChangeScreenLockPreferenceController extends AbstractPreferenceCont
protected RestrictedPreference mPreference;
public ChangeScreenLockPreferenceController(Context context, SecuritySettings host) {
public ChangeScreenLockPreferenceController(Context context, SettingsPreferenceFragment host) {
super(context);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);

View File

@@ -123,7 +123,7 @@ public class DsdsDialogActivity extends SubscriptionActionDialogActivity
DIALOG_TAG_ENABLE_DSDS_CONFIRMATION,
getString(R.string.sim_action_enable_dsds_title),
getString(R.string.sim_action_enable_dsds_text),
getString(R.string.sim_action_continue),
getString(R.string.sim_action_yes),
getString(R.string.sim_action_no_thanks));
}

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

@@ -17,11 +17,15 @@ package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.ContentValues;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.SystemBatteryConsumer;
import android.os.UserManager;
@@ -36,19 +40,26 @@ import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@RunWith(RobolectricTestRunner.class)
public final class BatteryDiffEntryTest {
private Context mContext;
@Mock
private UserManager mockUserManager;
@Mock private ApplicationInfo mockAppInfo;
@Mock private PackageManager mockPackageManager;
@Mock private UserManager mockUserManager;
@Mock private Drawable mockDrawable;
@Mock private Drawable mockDrawable2;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
doReturn(mockUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(mockPackageManager).when(mContext).getPackageManager();
BatteryDiffEntry.clearCache();
}
@Test
@@ -96,9 +107,8 @@ public final class BatteryDiffEntryTest {
@Test
public void testLoadLabelAndIcon_forSystemBattery_returnExpectedResult() {
// Generates fake testing data.
final ContentValues values = new ContentValues();
values.put("consumerType",
Integer.valueOf(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY));
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
values.put("drainType",
Integer.valueOf(SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY));
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
@@ -106,15 +116,15 @@ public final class BatteryDiffEntryTest {
final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
assertThat(entry.getAppLabel()).isEqualTo("Ambient display");
assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
}
@Test
public void testLoadLabelAndIcon_forUserBattery_returnExpectedResult() {
doReturn(null).when(mockUserManager).getUserInfo(1001);
// Generates fake testing data.
final ContentValues values = new ContentValues();
values.put("consumerType",
Integer.valueOf(ConvertUtils.CONSUMER_TYPE_USER_BATTERY));
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_USER_BATTERY);
values.put("userId", Integer.valueOf(1001));
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
@@ -122,6 +132,109 @@ public final class BatteryDiffEntryTest {
assertThat(entry.getAppLabel()).isEqualTo("Removed user");
assertThat(entry.getAppIcon()).isNull();
assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
}
@Test
public void testGetAppLabel_loadDataFromApplicationInfo() throws Exception {
final String expectedAppLabel = "fake app label";
final String fakePackageName = "com.fake.google.com";
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
values.put("uid", /*invalid uid*/ 10001);
values.put("packageName", fakePackageName);
doReturn(mockAppInfo).when(mockPackageManager)
.getApplicationInfo(fakePackageName, 0);
doReturn(expectedAppLabel).when(mockPackageManager)
.getApplicationLabel(mockAppInfo);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel);
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
// Verifies the app label in the cache.
final BatteryEntry.NameAndIcon nameAndIcon =
BatteryDiffEntry.sResourceCache.get(batteryHistEntry.getKey());
assertThat(nameAndIcon.name).isEqualTo(expectedAppLabel);
}
@Test
public void testGetAppLabel_loadDataFromPreDefinedNameAndUid() {
final String expectedAppLabel = "Android OS";
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel);
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
// Verifies the app label in the cache.
final BatteryEntry.NameAndIcon nameAndIcon =
BatteryDiffEntry.sResourceCache.get(batteryHistEntry.getKey());
assertThat(nameAndIcon.name).isEqualTo(expectedAppLabel);
}
@Test
public void testGetAppLabel_nullAppLabel_returnAppLabelInBatteryHistEntry() {
final String expectedAppLabel = "fake app label";
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
values.put("appLabel", expectedAppLabel);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
entry.mIsLoaded = true;
assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel);
assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
}
@Test
public void testGetAppIcon_nonUidConsumer_returnAppIconInBatteryDiffEntry() {
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
entry.mIsLoaded = true;
entry.mAppIcon = mockDrawable;
assertThat(entry.getAppIcon()).isEqualTo(mockDrawable);
assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
}
@Test
public void testGetAppIcon_uidConsumerWithNullIcon_returnDefaultActivityIcon()
throws Exception {
final BatteryDiffEntry entry = createBatteryDiffEntry(mockDrawable);
entry.mAppIcon = null;
assertThat(entry.getAppIcon()).isEqualTo(mockDrawable);
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
// Verifies the app label in the cache.
final BatteryEntry.NameAndIcon nameAndIcon =
BatteryDiffEntry.sResourceCache.get(entry.mBatteryHistEntry.getKey());
assertThat(nameAndIcon.icon).isEqualTo(mockDrawable);
}
@Test
public void testClearCache_switchLocale_clearCacheIconAndLabel() throws Exception {
Locale.setDefault(new Locale("en_US"));
final BatteryDiffEntry entry1 = createBatteryDiffEntry(mockDrawable);
assertThat(entry1.getAppIcon()).isEqualTo(mockDrawable);
// Switch the locale into another one.
Locale.setDefault(new Locale("zh_TW"));
final BatteryDiffEntry entry2 = createBatteryDiffEntry(mockDrawable2);
// We should get new drawable without caching.
assertThat(entry2.getAppIcon()).isEqualTo(mockDrawable2);
// Verifies the cache is updated into the new drawable.
final BatteryEntry.NameAndIcon nameAndIcon =
BatteryDiffEntry.sResourceCache.get(entry2.mBatteryHistEntry.getKey());
assertThat(nameAndIcon.icon).isEqualTo(mockDrawable2);
}
private BatteryDiffEntry createBatteryDiffEntry(
@@ -135,4 +248,23 @@ public final class BatteryDiffEntryTest {
entry.setTotalConsumePower(100.0);
return entry;
}
private static ContentValues getContentValuesWithType(int consumerType) {
final ContentValues values = new ContentValues();
values.put("consumerType", Integer.valueOf(consumerType));
return values;
}
private BatteryDiffEntry createBatteryDiffEntry(Drawable drawable) throws Exception {
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
values.put("uid", 1001);
values.put("packageName", "com.a.b.c");
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
doReturn(drawable).when(mockPackageManager).getDefaultActivityIcon();
doReturn(null).when(mockPackageManager).getApplicationInfo("com.a.b.c", 0);
doReturn(new String[] {"com.a.b.c"}).when(mockPackageManager)
.getPackagesForUid(1001);
return createBatteryDiffEntry(10, batteryHistEntry);
}
}

View File

@@ -137,6 +137,42 @@ public final class BatteryHistEntryTest {
/*percentOfTotal=*/ 0.3);
}
@Test
public void testGetKey_consumerUidType_returnExpectedString() {
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
values.put("uid", 3);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
assertThat(batteryHistEntry.getKey()).isEqualTo("3");
}
@Test
public void testGetKey_consumerUserType_returnExpectedString() {
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_USER_BATTERY);
values.put("userId", 2);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
assertThat(batteryHistEntry.getKey()).isEqualTo("U|2");
}
@Test
public void testGetKey_consumerSystemType_returnExpectedString() {
final ContentValues values = getContentValuesWithType(
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
values.put("drainType", 1);
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
assertThat(batteryHistEntry.getKey()).isEqualTo("S|1");
}
private static ContentValues getContentValuesWithType(int consumerType) {
final ContentValues values = new ContentValues();
values.put("consumerType", Integer.valueOf(consumerType));
return values;
}
private void assertBatteryHistEntry(
BatteryHistEntry entry, int drainType, double percentOfTotal) {
assertThat(entry.isValidEntry()).isTrue();
@@ -161,7 +197,5 @@ public final class BatteryHistEntryTest {
.isEqualTo(BatteryManager.BATTERY_STATUS_FULL);
assertThat(entry.mBatteryHealth)
.isEqualTo(BatteryManager.BATTERY_HEALTH_COLD);
assertThat(entry.getKey())
.isEqualTo("com.google.android.settings.battery-" + entry.mUserId);
}
}

View File

@@ -70,7 +70,7 @@ public class TranscodeDefaultOptionPreferenceControllerTest {
}
@Test
public void getAvailabilityStatus_shouldReturn_isAvailable() {
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2021 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.development.transcode;
import static com.android.settings.development.transcode.TranscodeDisableCachePreferenceController.TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.SystemProperties;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class TranscodeDisableCachePreferenceControllerTest {
private TranscodeUserControlPreferenceController mUnderTest;
@Before
public void setUp() {
Context context = ApplicationProvider.getApplicationContext();
mUnderTest = new TranscodeUserControlPreferenceController(context, "some_key");
}
@Test
public void isChecked_whenSysPropSet_shouldReturnTrue() {
SystemProperties.set(TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY, "true");
assertThat(mUnderTest.isChecked()).isTrue();
}
@Test
public void isChecked_whenSysPropUnset_shouldReturnFalse() {
SystemProperties.set(TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY, "false");
assertThat(mUnderTest.isChecked()).isFalse();
}
@Test
public void setChecked_withTrue_shouldSetSysProp() {
mUnderTest.setChecked(true);
assertThat(
SystemProperties.getBoolean(TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY, false)).isTrue();
}
@Test
public void setChecked_withFalse_shouldUnsetSysProp() {
mUnderTest.setChecked(false);
assertThat(
SystemProperties.getBoolean(TRANSCODE_DISABLE_CACHE_SYS_PROP_KEY, true)).isFalse();
}
@Test
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
}

View File

@@ -69,7 +69,7 @@ public class TranscodeNotificationPreferenceControllerTest {
}
@Test
public void getAvailabilityStatus_shouldReturn_isAvailable() {
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}

View File

@@ -70,7 +70,7 @@ public class TranscodeUserControlPreferenceControllerTest {
}
@Test
public void getAvailabilityStatus_shouldReturn_isAvailable() {
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}

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");
}
}