Snap for 7408786 from f8f435397c to sc-v2-release
Change-Id: I4cfef67d0e9da99a902fc114ab8cd9b942a8e034
This commit is contained in:
@@ -2448,7 +2448,7 @@
|
||||
|
||||
<activity
|
||||
android:name="com.android.settings.accounts.AddAccountSettings"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:theme="@style/Theme.SubSettings"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:exported="true"
|
||||
android:label="@string/header_add_an_account">
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M15,3V4H20V6H19V19C19,20.1 18.1,21 17,21H7C5.9,21 5,20.1 5,19V6H4V4H9V3H15ZM7,19H17V6H7V19ZM9,8H11V17H9V8ZM15,8H13V17H15V8Z"
|
||||
android:fillColor="#5F6368"
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
limitations under the License
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="true">
|
||||
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/container_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -38,5 +42,5 @@
|
||||
layout="@layout/accessibility_edit_shortcut_component" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
@@ -15,7 +15,12 @@
|
||||
limitations under the License
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="true">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/container_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -74,5 +79,5 @@
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
@@ -648,7 +648,7 @@
|
||||
<style name="AccessibilityDialogIcon">
|
||||
<item name="android:layout_width">18dp</item>
|
||||
<item name="android:layout_height">18dp</item>
|
||||
<item name="android:layout_marginRight">12dp</item>
|
||||
<item name="android:layout_marginEnd">12dp</item>
|
||||
<item name="android:scaleType">fitCenter</item>
|
||||
</style>
|
||||
|
||||
|
||||
@@ -312,6 +312,7 @@ public class AppLaunchSettings extends AppInfoBase implements
|
||||
/** Initialize add link preference */
|
||||
private void initAddLinkPreference() {
|
||||
mAddLinkPreference = findPreference(ADD_LINK_PREF_KEY);
|
||||
mAddLinkPreference.setVisible(isAddLinksShown());
|
||||
mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
|
||||
mAddLinkPreference.setOnPreferenceClickListener(preference -> {
|
||||
final int stateNoneLinksNo = getLinksNumber(DOMAIN_STATE_NONE);
|
||||
@@ -327,6 +328,10 @@ public class AppLaunchSettings extends AppInfoBase implements
|
||||
return getLinksNumber(DOMAIN_STATE_NONE) > 0;
|
||||
}
|
||||
|
||||
private boolean isAddLinksShown() {
|
||||
return (isAddLinksNotEmpty() || getLinksNumber(DOMAIN_STATE_SELECTED) > 0);
|
||||
}
|
||||
|
||||
private void showProgressDialogFragment() {
|
||||
final Bundle args = new Bundle();
|
||||
args.putString(APP_PACKAGE_KEY, mPackageName);
|
||||
|
||||
@@ -73,6 +73,7 @@ public class StorageCategoryFragment extends DashboardFragment
|
||||
LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.StorageResult>>,
|
||||
Preference.OnPreferenceClickListener {
|
||||
private static final String TAG = "StorageCategoryFrag";
|
||||
private static final String SELECTED_STORAGE_ENTRY_KEY = "selected_storage_entry_key";
|
||||
private static final String SUMMARY_PREF_KEY = "storage_summary";
|
||||
private static final String FREE_UP_SPACE_PREF_KEY = "free_up_space";
|
||||
private static final int STORAGE_JOB_ID = 0;
|
||||
@@ -127,6 +128,10 @@ public class StorageCategoryFragment extends DashboardFragment
|
||||
|
||||
mStorageManager = getActivity().getSystemService(StorageManager.class);
|
||||
|
||||
if (icicle != null) {
|
||||
mSelectedStorageEntry = icicle.getParcelable(SELECTED_STORAGE_ENTRY_KEY);
|
||||
}
|
||||
|
||||
initializePreference();
|
||||
}
|
||||
|
||||
@@ -167,6 +172,12 @@ public class StorageCategoryFragment extends DashboardFragment
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putParcelable(SELECTED_STORAGE_ENTRY_KEY, mSelectedStorageEntry);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
private void onReceivedSizes() {
|
||||
boolean stopLoading = false;
|
||||
if (mStorageInfo != null) {
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
package com.android.settings.deviceinfo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
@@ -26,6 +24,7 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.deviceinfo.storage.StorageUtils;
|
||||
|
||||
public class StorageItemPreference extends Preference {
|
||||
public int userHandle;
|
||||
@@ -49,7 +48,7 @@ public class StorageItemPreference extends Preference {
|
||||
|
||||
public void setStorageSize(long size, long total) {
|
||||
mStorageSize = size;
|
||||
setSummary(getStorageSummary(size));
|
||||
setSummary(StorageUtils.getStorageSizeLabel(getContext(), size));
|
||||
|
||||
if (total == 0) {
|
||||
mProgressPercent = 0;
|
||||
@@ -77,11 +76,4 @@ public class StorageItemPreference extends Preference {
|
||||
updateProgressBar();
|
||||
super.onBindViewHolder(view);
|
||||
}
|
||||
|
||||
private String getStorageSummary(long bytes) {
|
||||
final Formatter.BytesResult result = Formatter.formatBytes(getContext().getResources(),
|
||||
bytes, Formatter.FLAG_SHORTER);
|
||||
return TextUtils.expandTemplate(getContext().getText(R.string.storage_size_large),
|
||||
result.value, result.units).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,25 +18,54 @@ package com.android.settings.deviceinfo.storage;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
/**
|
||||
* Dialog asks if users want to empty trash files.
|
||||
* TODO(b/189388449): Shows "Deleting..." and disables Trash category while deleting trash files.
|
||||
*/
|
||||
public class EmptyTrashFragment extends InstrumentedDialogFragment {
|
||||
private static final String TAG = "EmptyTrashFragment";
|
||||
|
||||
private static final String TAG_EMPTY_TRASH = "empty_trash";
|
||||
|
||||
private final Fragment mParentFragment;
|
||||
private final int mUserId;
|
||||
private final long mTrashSize;
|
||||
private final OnEmptyTrashCompleteListener mOnEmptyTrashCompleteListener;
|
||||
|
||||
/** The listener to receive empty trash complete callback event. */
|
||||
public interface OnEmptyTrashCompleteListener {
|
||||
/** The empty trash complete callback. */
|
||||
void onEmptyTrashComplete();
|
||||
}
|
||||
|
||||
public EmptyTrashFragment(Fragment parent, int userId, long trashSize,
|
||||
OnEmptyTrashCompleteListener onEmptyTrashCompleteListener) {
|
||||
super();
|
||||
|
||||
mParentFragment = parent;
|
||||
setTargetFragment(mParentFragment, 0 /* requestCode */);
|
||||
mUserId = userId;
|
||||
mTrashSize = trashSize;
|
||||
mOnEmptyTrashCompleteListener = onEmptyTrashCompleteListener;
|
||||
}
|
||||
|
||||
/** Shows the empty trash dialog. */
|
||||
public static void show(Fragment parent) {
|
||||
final EmptyTrashFragment dialog = new EmptyTrashFragment();
|
||||
dialog.setTargetFragment(parent, 0 /* requestCode */);
|
||||
dialog.show(parent.getFragmentManager(), TAG_EMPTY_TRASH);
|
||||
public void show() {
|
||||
show(mParentFragment.getFragmentManager(), TAG_EMPTY_TRASH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,10 +77,38 @@ public class EmptyTrashFragment extends InstrumentedDialogFragment {
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
return builder.setTitle(R.string.storage_trash_dialog_title)
|
||||
.setMessage(R.string.storage_trash_dialog_ask_message)
|
||||
.setPositiveButton(R.string.storage_trash_dialog_confirm, (dialog, which) -> {
|
||||
// TODO(170918505): Implement the logic in worker thread.
|
||||
}).setNegativeButton(android.R.string.cancel, null)
|
||||
.setMessage(getActivity().getString(R.string.storage_trash_dialog_ask_message,
|
||||
StorageUtils.getStorageSizeLabel(getActivity(), mTrashSize)))
|
||||
.setPositiveButton(R.string.storage_trash_dialog_confirm,
|
||||
(dialog, which) -> emptyTrashAsync())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
private void emptyTrashAsync() {
|
||||
final Context context = getActivity();
|
||||
final Context perUserContext;
|
||||
try {
|
||||
perUserContext = context.createPackageContextAsUser(
|
||||
context.getApplicationContext().getPackageName(),
|
||||
0 /* flags= */,
|
||||
UserHandle.of(mUserId));
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(TAG, "Not able to get Context for user ID " + mUserId);
|
||||
return;
|
||||
}
|
||||
|
||||
final Bundle trashQueryArgs = new Bundle();
|
||||
trashQueryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_ONLY);
|
||||
ThreadUtils.postOnBackgroundThread(() -> {
|
||||
perUserContext.getContentResolver().delete(
|
||||
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
|
||||
trashQueryArgs);
|
||||
if (mOnEmptyTrashCompleteListener == null) {
|
||||
return;
|
||||
}
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> mOnEmptyTrashCompleteListener.onEmptyTrashComplete());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.os.UserManager;
|
||||
import android.os.storage.VolumeInfo;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -64,7 +65,8 @@ import java.util.Map;
|
||||
* categorization breakdown.
|
||||
*/
|
||||
public class StorageItemPreferenceController extends AbstractPreferenceController implements
|
||||
PreferenceControllerMixin {
|
||||
PreferenceControllerMixin,
|
||||
EmptyTrashFragment.OnEmptyTrashCompleteListener {
|
||||
private static final String TAG = "StorageItemPreference";
|
||||
|
||||
private static final String SYSTEM_FRAGMENT_TAG = "SystemInfo";
|
||||
@@ -256,8 +258,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
|
||||
mGamesPreference.setVisible(privateStoragePreferencesVisible);
|
||||
mDocumentsAndOtherPreference.setVisible(privateStoragePreferencesVisible);
|
||||
mSystemPreference.setVisible(privateStoragePreferencesVisible);
|
||||
// TODO(b/170918505): Shows trash category after trash category feature complete.
|
||||
mTrashPreference.setVisible(false);
|
||||
mTrashPreference.setVisible(privateStoragePreferencesVisible);
|
||||
|
||||
if (privateStoragePreferencesVisible) {
|
||||
final VolumeInfo sharedVolume = mSvp.findEmulatedForPrivate(mVolume);
|
||||
@@ -460,13 +461,29 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
|
||||
private void launchTrashIntent() {
|
||||
final Intent intent = new Intent("android.settings.VIEW_TRASH");
|
||||
|
||||
if (intent.resolveActivity(mPackageManager) == null) {
|
||||
EmptyTrashFragment.show(mFragment);
|
||||
if (mPackageManager.resolveActivityAsUser(intent, 0 /* flags */, mUserId) == null) {
|
||||
final long trashSize = mTrashPreference.getStorageSize();
|
||||
if (trashSize > 0) {
|
||||
new EmptyTrashFragment(mFragment, mUserId, trashSize,
|
||||
this /* onEmptyTrashCompleteListener */).show();
|
||||
} else {
|
||||
Toast.makeText(mContext, R.string.storage_trash_dialog_empty_message,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
mContext.startActivityAsUser(intent, new UserHandle(mUserId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmptyTrashComplete() {
|
||||
if (mTrashPreference == null) {
|
||||
return;
|
||||
}
|
||||
mTrashPreference.setStorageSize(0, mTotalSize);
|
||||
updatePrivateStorageCategoryPreferencesOrder();
|
||||
}
|
||||
|
||||
private static long totalValues(StorageMeasurement.MeasurementDetails details, int userId,
|
||||
String... keys) {
|
||||
long total = 0;
|
||||
|
||||
@@ -26,6 +26,8 @@ import android.os.storage.DiskInfo;
|
||||
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.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -115,6 +117,14 @@ public class StorageUtils {
|
||||
.launch();
|
||||
}
|
||||
|
||||
/** Returns size label of changing units. (e.g., 1kB, 2MB, 3GB) */
|
||||
public static String getStorageSizeLabel(Context context, long bytes) {
|
||||
final Formatter.BytesResult result = Formatter.formatBytes(context.getResources(),
|
||||
bytes, Formatter.FLAG_SHORTER);
|
||||
return TextUtils.expandTemplate(context.getText(R.string.storage_size_large),
|
||||
result.value, result.units).toString();
|
||||
}
|
||||
|
||||
/** An AsyncTask to unmount a specified volume. */
|
||||
public static class UnmountTask extends AsyncTask<Void, Void, Exception> {
|
||||
private final Context mContext;
|
||||
|
||||
@@ -55,11 +55,14 @@ import java.util.List;
|
||||
* implementation.
|
||||
*/
|
||||
@SearchIndexable
|
||||
public class LocationSettings extends DashboardFragment {
|
||||
public class LocationSettings extends DashboardFragment implements
|
||||
LocationEnabler.LocationModeChangeListener {
|
||||
|
||||
private static final String TAG = "LocationSettings";
|
||||
private static final String RECENT_LOCATION_ACCESS_PREF_KEY = "recent_location_access";
|
||||
|
||||
private LocationSwitchBarController mSwitchBarController;
|
||||
private LocationEnabler mLocationEnabler;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -75,6 +78,7 @@ public class LocationSettings extends DashboardFragment {
|
||||
switchBar.show();
|
||||
mSwitchBarController = new LocationSwitchBarController(activity, switchBar,
|
||||
getSettingsLifecycle());
|
||||
mLocationEnabler = new LocationEnabler(getContext(), this, getSettingsLifecycle());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,6 +102,13 @@ public class LocationSettings extends DashboardFragment {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationModeChanged(int mode, boolean restricted) {
|
||||
if (mLocationEnabler.isEnabled(mode)) {
|
||||
scrollToPreference(RECENT_LOCATION_ACCESS_PREF_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
static void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) {
|
||||
// If there's some items to display, sort the items and add them to the container.
|
||||
Collections.sort(prefs,
|
||||
|
||||
@@ -109,24 +109,30 @@ public class LocationSettingsFooterPreferenceControllerTest {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the footer even without the injected string.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_noSystemApp_returnsFalse() {
|
||||
public void isAvailable_noSystemApp_returnsTrue() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the footer even without the injected string.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_noRequiredMetadata_returnsFalse() {
|
||||
public void isAvailable_noRequiredMetadata_returnsTrue() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ false));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,7 +160,8 @@ public class LocationSettingsFooterPreferenceControllerTest {
|
||||
mController.onLocationModeChanged(/* mode= */ 0, /* restricted= */ false);
|
||||
ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mFooterPreference, times(2)).setTitle(title.capture());
|
||||
assertThat(title.getValue().toString()).isEqualTo(
|
||||
|
||||
assertThat(title.getValue().toString()).contains(
|
||||
Html.fromHtml(mContext.getString(
|
||||
R.string.location_settings_footer_location_off)).toString());
|
||||
}
|
||||
@@ -171,7 +178,7 @@ public class LocationSettingsFooterPreferenceControllerTest {
|
||||
mController.onLocationModeChanged(/* mode= */ 1, /* restricted= */ false);
|
||||
ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mFooterPreference, times(2)).setTitle(title.capture());
|
||||
assertThat(title.getValue().toString()).isNotEqualTo(
|
||||
assertThat(title.getValue().toString()).doesNotContain(
|
||||
Html.fromHtml(mContext.getString(
|
||||
R.string.location_settings_footer_location_off)).toString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user