Snap for 7286185 from acb5bebb3d to sc-release
Change-Id: I063f14b65ac6598e9a86b22ca96325c47d19d713
This commit is contained in:
@@ -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"
|
||||
|
||||
63
res/drawable/ic_reduce_bright_colors.xml
Normal file
63
res/drawable/ic_reduce_bright_colors.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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] -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 */ }
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class TranscodeDefaultOptionPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_shouldReturn_isAvailable() {
|
||||
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
|
||||
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class TranscodeNotificationPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_shouldReturn_isAvailable() {
|
||||
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
|
||||
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class TranscodeUserControlPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_shouldReturn_isAvailable() {
|
||||
public void getAvailabilityStatus_shouldReturnAVAILABLE() {
|
||||
assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user