diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e3decb0f1e3..7702257c060 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2700,6 +2700,22 @@
android:value="com.android.settings.notification.AppNotificationSettings" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_settings_accessibility.xml b/res/drawable/ic_settings_accessibility.xml
index 7054d78073c..4cf518254d5 100644
--- a/res/drawable/ic_settings_accessibility.xml
+++ b/res/drawable/ic_settings_accessibility.xml
@@ -21,10 +21,5 @@
android:tint="?android:attr/colorControlNormal">
+ android:pathData="M20.75,6.99c-0.14,-0.55 -0.69,-0.87 -1.24,-0.75C17.13,6.77 14.48,7 12,7S6.87,6.77 4.49,6.24c-0.55,-0.12 -1.1,0.2 -1.24,0.75l0,0C3.11,7.55 3.45,8.12 4,8.25C5.61,8.61 7.35,8.86 9,9v12c0,0.55 0.45,1 1,1h0c0.55,0 1,-0.45 1,-1v-5h2v5c0,0.55 0.45,1 1,1h0c0.55,0 1,-0.45 1,-1V9c1.65,-0.14 3.39,-0.39 5,-0.75C20.55,8.12 20.89,7.55 20.75,6.99L20.75,6.99zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2s-2,0.9 -2,2S10.9,6 12,6z"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 197202ed75c..6241850e4ec 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6836,11 +6836,14 @@
Notification category
+
+ Notification category group
+
Importance
-
- Let the app decide
+
+ Allow sound
Never show notifications
@@ -7003,12 +7006,21 @@
Android is blocking this category of notifications from appearing on this device
+
+ Android is blocking this group of notifications from appearing on this device
+
Categories
Other
+
+
+ - %d category
+ - %d categories
+
+
This app has not posted any notifications
diff --git a/res/xml/upgraded_channel_notification_settings.xml b/res/xml/upgraded_channel_notification_settings.xml
index 2cece9e62ff..ee23435576b 100644
--- a/res/xml/upgraded_channel_notification_settings.xml
+++ b/res/xml/upgraded_channel_notification_settings.xml
@@ -38,6 +38,7 @@
settings:useAdditionalSummary="true" />
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index ddcdd8c3a2b..e61f9594dac 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -660,7 +660,11 @@ public class ApnEditor extends SettingsPreferenceFragment
return null;
} else {
String[] values = mRes.getStringArray(R.array.mvno_type_entries);
- mMvnoMatchData.setEnabled(mvnoIndex != 0);
+ boolean mvnoMatchDataUneditable =
+ mReadOnlyApn || (mReadOnlyApnFields != null
+ && Arrays.asList(mReadOnlyApnFields)
+ .contains(Telephony.Carriers.MVNO_MATCH_DATA));
+ mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0);
if (newValue != null && newValue.equals(oldValue) == false) {
if (values[mvnoIndex].equals("SPN")) {
mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName());
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index a6961726550..1ec4284c9fc 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -21,7 +21,6 @@ import android.os.Bundle;
import com.android.settings.applications.AppOpsSummary;
import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.fingerprint.FingerprintEnrollIntroduction;
-import com.android.settings.fingerprint.FingerprintSettings;
import com.android.settings.password.ChooseLockGeneric;
/**
@@ -124,10 +123,14 @@ public class Settings extends SettingsActivity {
public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ChannelNotificationSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class ChannelGroupNotificationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ManageDomainUrlsActivity extends SettingsActivity { /* empty */ }
public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ }
public static class GamesStorageActivity extends SettingsActivity { /* empty */ }
public static class MoviesStorageActivity extends SettingsActivity { /* empty */ }
+ public static class PhotosStorageActivity extends SettingsActivity {
+ /* empty */
+ }
public static class TopLevelSettings extends SettingsActivity { /* empty */ }
public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 7cc47e0c4a7..11eb0ccefd1 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -59,6 +59,7 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
+import com.android.settings.Settings;
import com.android.settings.Settings.AllApplicationsActivity;
import com.android.settings.Settings.GamesStorageActivity;
import com.android.settings.Settings.HighPowerApplicationsActivity;
@@ -115,6 +116,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
public static final String EXTRA_VOLUME_UUID = "volumeUuid";
public static final String EXTRA_VOLUME_NAME = "volumeName";
public static final String EXTRA_STORAGE_TYPE = "storageType";
+ public static final String EXTRA_WORK_ONLY = "workProfileOnly";
private static final String EXTRA_SORT_ORDER = "sortOrder";
private static final String EXTRA_SHOW_SYSTEM = "showSystem";
@@ -218,6 +220,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
public static final int STORAGE_TYPE_DEFAULT = 0; // Show all apps that are not categorized.
public static final int STORAGE_TYPE_MUSIC = 1;
public static final int STORAGE_TYPE_LEGACY = 2; // Show apps even if they can be categorized.
+ public static final int STORAGE_TYPE_PHOTOS_VIDEOS = 3;
// sort order
private int mSortOrder = R.id.sort_order_alpha;
@@ -261,6 +264,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
public static final int LIST_TYPE_MANAGE_SOURCES = 8;
public static final int LIST_TYPE_GAMES = 9;
public static final int LIST_TYPE_MOVIES = 10;
+ public static final int LIST_TYPE_PHOTOGRAPHY = 11;
// List types that should show instant apps.
@@ -277,6 +281,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
private ResetAppsHelper mResetAppsHelper;
private String mVolumeUuid;
private int mStorageType;
+ private boolean mIsWorkOnly;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -324,10 +329,15 @@ public class ManageApplications extends InstrumentedPreferenceFragment
} else if (className.equals(MoviesStorageActivity.class.getName())) {
mListType = LIST_TYPE_MOVIES;
mSortOrder = R.id.sort_order_size;
+ } else if (className.equals(Settings.PhotosStorageActivity.class.getName())) {
+ mListType = LIST_TYPE_PHOTOGRAPHY;
+ mSortOrder = R.id.sort_order_size;
+ mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT);
} else {
mListType = LIST_TYPE_MAIN;
}
mFilter = getDefaultFilter();
+ mIsWorkOnly = args != null ? args.getBoolean(EXTRA_WORK_ONLY) : false;
if (savedInstanceState != null) {
mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
@@ -375,6 +385,14 @@ public class ManageApplications extends InstrumentedPreferenceFragment
new StorageStatsSource(context),
mVolumeUuid,
UserHandle.of(UserHandle.getUserId(mCurrentUid))));
+ } else if (mStorageType == STORAGE_TYPE_PHOTOS_VIDEOS) {
+ Context context = getContext();
+ mApplications.setExtraViewController(
+ new PhotosViewHolderController(
+ context,
+ new StorageStatsSource(context),
+ mVolumeUuid,
+ UserHandle.of(UserHandle.getUserId(mCurrentUid))));
}
mListView.setAdapter(mApplications);
mListView.setRecyclerListener(mApplications);
@@ -423,6 +441,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment
}
AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid);
+ if (mIsWorkOnly) {
+ compositeFilter = new CompoundFilter(compositeFilter, FILTERS[FILTER_APPS_WORK]);
+ }
if (compositeFilter != null) {
mApplications.setCompositeFilter(compositeFilter);
}
@@ -444,6 +465,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter);
} else if (listType == LIST_TYPE_MOVIES) {
return new CompoundFilter(ApplicationsState.FILTER_MOVIES, filter);
+ } else if (listType == LIST_TYPE_PHOTOGRAPHY) {
+ return new CompoundFilter(ApplicationsState.FILTER_PHOTOS, filter);
}
return null;
@@ -473,6 +496,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
case LIST_TYPE_STORAGE:
case LIST_TYPE_GAMES:
case LIST_TYPE_MOVIES:
+ case LIST_TYPE_PHOTOGRAPHY:
return mSortOrder == R.id.sort_order_alpha;
default:
return false;
@@ -495,6 +519,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
return MetricsEvent.APPLICATIONS_STORAGE_GAMES;
case LIST_TYPE_MOVIES:
return MetricsEvent.APPLICATIONS_STORAGE_MOVIES;
+ case LIST_TYPE_PHOTOGRAPHY:
+ return MetricsEvent.APPLICATIONS_STORAGE_PHOTOS;
case LIST_TYPE_USAGE_ACCESS:
return MetricsEvent.USAGE_ACCESS;
case LIST_TYPE_HIGH_POWER:
@@ -598,6 +624,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment
case LIST_TYPE_MOVIES:
startAppInfoFragment(AppStorageSettings.class, R.string.storage_movies_tv);
break;
+ case LIST_TYPE_PHOTOGRAPHY:
+ startAppInfoFragment(AppStorageSettings.class, R.string.storage_photos_videos);
+ break;
// TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed
// app. Maybe when they load the list of apps that contains managed profile apps.
diff --git a/src/com/android/settings/applications/PhotosViewHolderController.java b/src/com/android/settings/applications/PhotosViewHolderController.java
new file mode 100644
index 00000000000..a652bb15159
--- /dev/null
+++ b/src/com/android/settings/applications/PhotosViewHolderController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.applications;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.InsetDrawable;
+import android.os.UserHandle;
+import android.support.annotation.WorkerThread;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settingslib.applications.StorageStatsSource;
+
+import java.io.IOException;
+
+/** PhotosViewHolderController controls an Audio/Music file view in the ManageApplications view. */
+public class PhotosViewHolderController implements FileViewHolderController {
+ private static final String TAG = "PhotosViewHolderController";
+
+ private static final String IMAGE_MIME_TYPE = "image/*";
+ private static final int INSET_SIZE = 24; // dp
+
+ private Context mContext;
+ private StorageStatsSource mSource;
+ private String mVolumeUuid;
+ private long mFilesSize;
+ private UserHandle mUser;
+
+ public PhotosViewHolderController(
+ Context context, StorageStatsSource source, String volumeUuid, UserHandle user) {
+ mContext = context;
+ mSource = source;
+ mVolumeUuid = volumeUuid;
+ mUser = user;
+ }
+
+ @Override
+ @WorkerThread
+ public void queryStats() {
+ try {
+ StorageStatsSource.ExternalStorageStats stats =
+ mSource.getExternalStorageStats(mVolumeUuid, mUser);
+ mFilesSize = stats.imageBytes + stats.videoBytes;
+ } catch (IOException e) {
+ mFilesSize = 0;
+ Log.w(TAG, e);
+ }
+ }
+
+ @Override
+ public boolean shouldShow() {
+ return true;
+ }
+
+ @Override
+ public void setupView(AppViewHolder holder) {
+ holder.appIcon.setImageDrawable(
+ new InsetDrawable(mContext.getDrawable(R.drawable.ic_photo_library), INSET_SIZE));
+ holder.appName.setText(mContext.getText(R.string.storage_detail_images));
+ holder.summary.setText(Formatter.formatFileSize(mContext, mFilesSize));
+ }
+
+ @Override
+ public void onClick(Fragment fragment) {
+ Intent intent = new Intent();
+ intent.setAction(android.content.Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ intent.setType(IMAGE_MIME_TYPE);
+ intent.putExtra(Intent.EXTRA_FROM_STORAGE, true);
+ Utils.launchIntent(fragment, intent);
+ }
+}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
index d674522873b..34d13388ed9 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
@@ -189,9 +189,7 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
.queryIntentServices(AUTOFILL_PROBE, PackageManager.GET_META_DATA);
for (ResolveInfo info : resolveInfos) {
final String permission = info.serviceInfo.permission;
- // TODO(b/37563972): remove BIND_AUTOFILL once clients use BIND_AUTOFILL_SERVICE
- if (Manifest.permission.BIND_AUTOFILL_SERVICE.equals(permission)
- || Manifest.permission.BIND_AUTOFILL.equals(permission)) {
+ if (Manifest.permission.BIND_AUTOFILL_SERVICE.equals(permission)) {
candidates.add(new DefaultAppInfo(mPm, mUserId, new ComponentName(
info.serviceInfo.packageName, info.serviceInfo.name)));
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
index 97382c3c480..22cb3a683ec 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
@@ -108,12 +108,6 @@ public class BluetoothPairingDialog extends Activity {
@VisibleForTesting
void dismiss() {
if (!isFinishing()) {
- BluetoothPairingDialogFragment bluetoothFragment =
- (BluetoothPairingDialogFragment) getFragmentManager()
- .findFragmentByTag(FRAGMENT_TAG);
- if (bluetoothFragment != null) {
- bluetoothFragment.dismiss();
- }
finish();
}
}
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 380c0704a7a..a03314ca2e2 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -100,6 +100,7 @@ import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.ChannelNotificationSettings;
+import com.android.settings.notification.ChannelGroupNotificationSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationAccessSettings;
import com.android.settings.notification.NotificationStation;
@@ -209,6 +210,7 @@ public class SettingsGateway {
BatterySaverSettings.class.getName(),
AppNotificationSettings.class.getName(),
ChannelNotificationSettings.class.getName(),
+ ChannelGroupNotificationSettings.class.getName(),
ApnSettings.class.getName(),
ApnEditor.class.getName(),
WifiCallingSettings.class.getName(),
diff --git a/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java b/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java
index e8e2c2d8da1..a4f087a97f2 100644
--- a/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java
+++ b/src/com/android/settings/development/CameraHalHdrplusPreferenceController.java
@@ -102,6 +102,6 @@ public class CameraHalHdrplusPreferenceController extends AbstractPreferenceCont
}
private boolean isHalHdrplusEnabled() {
- return SystemProperties.getBoolean(PROPERTY_CAMERA_HAL_HDRPLUS, false);
+ return SystemProperties.getBoolean(PROPERTY_CAMERA_HAL_HDRPLUS, true);
}
}
diff --git a/src/com/android/settings/deviceinfo/StorageProfileFragment.java b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
index 7a0a59e35e2..9f3ce0ca8d0 100644
--- a/src/com/android/settings/deviceinfo/StorageProfileFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
@@ -101,8 +101,13 @@ public class StorageProfileFragment extends DashboardFragment
protected List getPreferenceControllers(Context context) {
final List controllers = new ArrayList<>();
final StorageManager sm = context.getSystemService(StorageManager.class);
- mPreferenceController = new StorageItemPreferenceController(context, this,
- mVolume, new StorageManagerVolumeProvider(sm));
+ mPreferenceController =
+ new StorageItemPreferenceController(
+ context,
+ this,
+ mVolume,
+ new StorageManagerVolumeProvider(sm),
+ /* isWorkProfile */ true);
controllers.add(mPreferenceController);
return controllers;
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index 630df8576ab..f92a24e9f9d 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -18,6 +18,7 @@ package com.android.settings.deviceinfo.storage;
import static android.content.pm.ApplicationInfo.CATEGORY_AUDIO;
import static android.content.pm.ApplicationInfo.CATEGORY_GAME;
+import static android.content.pm.ApplicationInfo.CATEGORY_IMAGE;
import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
import android.content.Context;
@@ -134,6 +135,9 @@ public class StorageAsyncLoader
case CATEGORY_VIDEO:
result.videoAppsSize += blamedSize;
break;
+ case CATEGORY_IMAGE:
+ result.photosAppsSize += blamedSize;
+ break;
default:
// The deprecated game flag does not set the category.
if ((app.flags & ApplicationInfo.FLAG_IS_GAME) != 0) {
@@ -163,6 +167,7 @@ public class StorageAsyncLoader
public static class AppsStorageResult {
public long gamesSize;
public long musicAppsSize;
+ public long photosAppsSize;
public long videoAppsSize;
public long otherAppsSize;
public long cacheSize;
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 7060779ae65..ca85f69d0be 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -59,7 +59,6 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
PreferenceControllerMixin {
private static final String TAG = "StorageItemPreference";
- private static final String IMAGE_MIME_TYPE = "image/*";
private static final String SYSTEM_FRAGMENT_TAG = "SystemInfo";
@VisibleForTesting
@@ -93,6 +92,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private StorageItemPreference mAppPreference;
private StorageItemPreference mFilePreference;
private StorageItemPreference mSystemPreference;
+ private boolean mIsWorkProfile;
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
@@ -106,6 +106,16 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
mUserId = UserHandle.myUserId();
}
+ public StorageItemPreferenceController(
+ Context context,
+ Fragment hostFragment,
+ VolumeInfo volume,
+ StorageVolumeProvider svp,
+ boolean isWorkProfile) {
+ this(context, hostFragment, volume, svp);
+ mIsWorkProfile = isWorkProfile;
+ }
+
@Override
public boolean isAvailable() {
return true;
@@ -212,7 +222,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
if (preference != null) {
Drawable currentIcon = preference.getIcon();
// Sigh... Applying the badge to the icon clobbers the tint on the base drawable.
- // For some reason, re-applying it here means the tint remains.
+ // For some reason, reapplying it here means the tint remains.
currentIcon = applyTint(mContext, currentIcon);
preference.setIcon(pm.getUserBadgedIcon(currentIcon, userHandle));
}
@@ -220,7 +230,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private static Drawable applyTint(Context context, Drawable icon) {
TypedArray array =
- context.obtainStyledAttributes(new int[]{android.R.attr.colorControlNormal});
+ context.obtainStyledAttributes(new int[] {android.R.attr.colorControlNormal});
icon = icon.mutate();
icon.setTint(array.getColor(0, 0));
array.recycle();
@@ -248,7 +258,8 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
// TODO(b/35927909): Figure out how to split out apps which are only installed for work
// profiles in order to attribute those app's code bytes only to that profile.
mPhotoPreference.setStorageSize(
- data.externalStats.imageBytes + data.externalStats.videoBytes, mTotalSize);
+ data.photosAppsSize + data.externalStats.imageBytes + data.externalStats.videoBytes,
+ mTotalSize);
mAudioPreference.setStorageSize(
data.musicAppsSize + data.externalStats.audioBytes, mTotalSize);
mGamePreference.setStorageSize(data.gamesSize, mTotalSize);
@@ -269,10 +280,12 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
long attributedSize = 0;
for (int i = 0; i < result.size(); i++) {
final StorageAsyncLoader.AppsStorageResult otherData = result.valueAt(i);
- attributedSize += otherData.gamesSize
- + otherData.musicAppsSize
- + otherData.videoAppsSize
- + otherData.otherAppsSize;
+ attributedSize +=
+ otherData.gamesSize
+ + otherData.musicAppsSize
+ + otherData.videoAppsSize
+ + otherData.photosAppsSize
+ + otherData.otherAppsSize;
attributedSize += otherData.externalStats.totalBytes
- otherData.externalStats.appBytes;
}
@@ -306,12 +319,21 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
}
private Intent getPhotosIntent() {
- Intent intent = new Intent();
- intent.setAction(android.content.Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- intent.setType(IMAGE_MIME_TYPE);
- intent.putExtra(Intent.EXTRA_FROM_STORAGE, true);
- return intent;
+ Bundle args = new Bundle(2);
+ args.putString(
+ ManageApplications.EXTRA_CLASSNAME, Settings.PhotosStorageActivity.class.getName());
+ args.putInt(
+ ManageApplications.EXTRA_STORAGE_TYPE,
+ ManageApplications.STORAGE_TYPE_PHOTOS_VIDEOS);
+ return Utils.onBuildStartFragmentIntent(
+ mContext,
+ ManageApplications.class.getName(),
+ args,
+ null,
+ R.string.storage_photos_videos,
+ null,
+ false,
+ mMetricsFeatureProvider.getMetricsCategory(mFragment));
}
private Intent getAudioIntent() {
@@ -320,6 +342,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
}
Bundle args = new Bundle();
+ args.putBoolean(ManageApplications.EXTRA_WORK_ONLY, mIsWorkProfile);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.StorageUseActivity.class.getName());
args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
@@ -336,6 +359,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
}
Bundle args = new Bundle();
+ args.putBoolean(ManageApplications.EXTRA_WORK_ONLY, mIsWorkProfile);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.StorageUseActivity.class.getName());
args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
@@ -347,6 +371,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private Intent getGamesIntent() {
Bundle args = new Bundle(1);
+ args.putBoolean(ManageApplications.EXTRA_WORK_ONLY, mIsWorkProfile);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.GamesStorageActivity.class.getName());
return Utils.onBuildStartFragmentIntent(mContext,
@@ -356,6 +381,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private Intent getMoviesIntent() {
Bundle args = new Bundle(1);
+ args.putBoolean(ManageApplications.EXTRA_WORK_ONLY, mIsWorkProfile);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.MoviesStorageActivity.class.getName());
return Utils.onBuildStartFragmentIntent(mContext,
diff --git a/src/com/android/settings/deviceinfo/storage/UserProfileController.java b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
index 684ac52352b..cf1e3603bbe 100644
--- a/src/com/android/settings/deviceinfo/storage/UserProfileController.java
+++ b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
@@ -19,6 +19,7 @@ package com.android.settings.deviceinfo.storage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.storage.VolumeInfo;
@@ -126,7 +127,14 @@ public class UserProfileController extends AbstractPreferenceController implemen
public void handleUserIcons(SparseArray fetchedIcons) {
Drawable userIcon = fetchedIcons.get(mUser.id);
if (userIcon != null) {
- mStoragePreference.setIcon(userIcon);
+ mStoragePreference.setIcon(applyTint(mContext, userIcon));
}
}
+
+ private static Drawable applyTint(Context context, Drawable icon) {
+ icon = icon.mutate();
+ icon.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
+ return icon;
+ }
+
}
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 78a0a74999f..29eb4a3098e 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -50,7 +51,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -105,7 +105,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
new AsyncTask() {
@Override
protected Void doInBackground(Void... unused) {
- mChannelGroupList = mBackend.getChannelGroups(mPkg, mUid).getList();
+ mChannelGroupList = mBackend.getGroups(mPkg, mUid).getList();
Collections.sort(mChannelGroupList, mChannelGroupComparator);
return null;
}
@@ -115,7 +115,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
if (getHost() == null) {
return;
}
- populateChannelList();
+ populateList();
addAppLinkPref();
}
}.execute();
@@ -144,7 +144,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
getPreferenceScreen().addPreference(pref);
}
- private void populateChannelList() {
+ private void populateList() {
if (!mChannelGroups.isEmpty()) {
// If there's anything in mChannelGroups, we've called populateChannelList twice.
// Clear out existing channels and log.
@@ -166,30 +166,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
empty.setEnabled(false);
groupCategory.addPreference(empty);
} else {
- for (NotificationChannelGroup group : mChannelGroupList) {
- PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
- if (group.getId() == null) {
- groupCategory.setTitle(mChannelGroupList.size() > 1
- ? R.string.notification_channels_other
- : R.string.notification_channels);
- groupCategory.setKey(KEY_GENERAL_CATEGORY);
- } else {
- groupCategory.setTitle(group.getName());
- groupCategory.setKey(group.getId());
- }
- groupCategory.setOrderingAsAdded(true);
- getPreferenceScreen().addPreference(groupCategory);
- mChannelGroups.add(groupCategory);
-
- final List channels = group.getChannels();
- Collections.sort(channels, mChannelComparator);
- int N = channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel channel = channels.get(i);
- populateSingleChannelPrefs(groupCategory, channel);
- }
- }
-
+ populateGroupList();
int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
if (deletedChannelCount > 0) {
mDeletedChannels = new FooterPreference(getPrefContext());
@@ -202,48 +179,63 @@ public class AppNotificationSettings extends NotificationSettingsBase {
getPreferenceScreen().addPreference(mDeletedChannels);
}
}
-
updateDependents(mAppRow.banned);
}
- private void populateSingleChannelPrefs(PreferenceCategory groupCategory,
- final NotificationChannel channel) {
- MasterSwitchPreference channelPref = new MasterSwitchPreference(
+ private void populateGroupList() {
+ PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
+ groupCategory.setTitle(R.string.notification_channels);
+ groupCategory.setKey(KEY_GENERAL_CATEGORY);
+ groupCategory.setOrderingAsAdded(true);
+ getPreferenceScreen().addPreference(groupCategory);
+ mChannelGroups.add(groupCategory);
+ for (NotificationChannelGroup group : mChannelGroupList) {
+ final List channels = group.getChannels();
+ int N = channels.size();
+ // app defined groups with one channel and channels with no group display the channel
+ // name and no summary and link directly to the channel page unless the group is blocked
+ if ((group.getId() == null || N < 2) && !group.isBlocked()) {
+ Collections.sort(channels, mChannelComparator);
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel channel = channels.get(i);
+ populateSingleChannelPrefs(groupCategory, channel, "");
+ }
+ } else {
+ populateGroupPreference(groupCategory, group, N);
+ }
+ }
+ }
+
+ void populateGroupPreference(PreferenceGroup parent,
+ final NotificationChannelGroup group, int channelCount) {
+ MasterSwitchPreference groupPref = new MasterSwitchPreference(
getPrefContext());
- channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null
- && isChannelBlockable(mAppRow.systemApp, channel)
- && isChannelConfigurable(channel));
- channelPref.setKey(channel.getId());
- channelPref.setTitle(channel.getName());
- channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
- channelPref.setSummary(getImportanceSummary(channel));
- Bundle channelArgs = new Bundle();
- channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
- channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
- channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
- Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
- ChannelNotificationSettings.class.getName(),
- channelArgs, null, R.string.notification_channel_title, null, false,
+ groupPref.setSwitchEnabled(mSuspendedAppsAdmin == null
+ && isChannelGroupBlockable(group));
+ groupPref.setKey(group.getId());
+ groupPref.setTitle(group.getName());
+ groupPref.setChecked(!group.isBlocked());
+ groupPref.setSummary(getResources().getQuantityString(
+ R.plurals.notification_group_summary, channelCount, channelCount));
+ Bundle groupArgs = new Bundle();
+ groupArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+ groupArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+ groupArgs.putString(Settings.EXTRA_CHANNEL_GROUP_ID, group.getId());
+ Intent groupIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+ ChannelGroupNotificationSettings.class.getName(),
+ groupArgs, null, R.string.notification_group_title, null, false,
getMetricsCategory());
- channelPref.setIntent(channelIntent);
+ groupPref.setIntent(groupIntent);
- channelPref.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference,
- Object o) {
- boolean value = (Boolean) o;
- int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
- channel.setImportance(importance);
- channel.lockFields(
- NotificationChannel.USER_LOCKED_IMPORTANCE);
- channelPref.setSummary(getImportanceSummary(channel));
- mBackend.updateChannel(mPkg, mUid, channel);
+ groupPref.setOnPreferenceChangeListener(
+ (preference, o) -> {
+ boolean value = (Boolean) o;
+ group.setBlocked(!value);
+ mBackend.updateChannelGroup(mPkg, mUid, group);
- return true;
- }
+ return true;
});
- groupCategory.addPreference(channelPref);
+ parent.addPreference(groupPref);
}
void setupBadge() {
@@ -330,38 +322,6 @@ public class AppNotificationSettings extends NotificationSettingsBase {
}
}
- private String getImportanceSummary(NotificationChannel channel) {
- switch (channel.getImportance()) {
- case NotificationManager.IMPORTANCE_UNSPECIFIED:
- return getContext().getString(R.string.notification_importance_unspecified);
- case NotificationManager.IMPORTANCE_NONE:
- return getContext().getString(R.string.notification_toggle_off);
- case NotificationManager.IMPORTANCE_MIN:
- return getContext().getString(R.string.notification_importance_min);
- case NotificationManager.IMPORTANCE_LOW:
- return getContext().getString(R.string.notification_importance_low);
- case NotificationManager.IMPORTANCE_DEFAULT:
- return getContext().getString(R.string.notification_importance_default);
- case NotificationManager.IMPORTANCE_HIGH:
- case NotificationManager.IMPORTANCE_MAX:
- default:
- return getContext().getString(R.string.notification_importance_high);
- }
-
- }
-
- private Comparator mChannelComparator =
- new Comparator() {
-
- @Override
- public int compare(NotificationChannel left, NotificationChannel right) {
- if (left.isDeleted() != right.isDeleted()) {
- return Boolean.compare(left.isDeleted(), right.isDeleted());
- }
- return left.getId().compareTo(right.getId());
- }
- };
-
private Comparator mChannelGroupComparator =
new Comparator() {
diff --git a/src/com/android/settings/notification/ChannelGroupNotificationSettings.java b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
new file mode 100644
index 00000000000..7837ec866df
--- /dev/null
+++ b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
@@ -0,0 +1,188 @@
+/*
+ * 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.notification;
+
+import android.app.Activity;
+import android.app.NotificationChannel;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.widget.FooterPreference;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ChannelGroupNotificationSettings extends NotificationSettingsBase {
+ private static final String TAG = "ChannelGroupSettings";
+
+ private static String KEY_DELETED = "deleted";
+
+ private EntityHeaderController mHeaderPref;
+ private List mChannels = new ArrayList();
+ private FooterPreference mDeletedChannels;
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.NOTIFICATION_CHANNEL_GROUP;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannelGroup == null) {
+ Log.w(TAG, "Missing package or uid or packageinfo or group");
+ finish();
+ return;
+ }
+
+ if (getPreferenceScreen() != null) {
+ getPreferenceScreen().removeAll();
+ }
+ addPreferencesFromResource(R.xml.notification_settings);
+ setupBlock();
+ addHeaderPref();
+ addAppLinkPref();
+ addFooterPref();
+ populateChannelList();
+
+ updateDependents(mChannelGroup.isBlocked());
+ }
+
+ @Override
+ void setupBadge() {
+
+ }
+
+ private void populateChannelList() {
+ if (!mChannels.isEmpty()) {
+ // If there's anything in mChannels, we've called populateChannelList twice.
+ // Clear out existing channels and log.
+ Log.w(TAG, "Notification channel group posted twice to settings - old size " +
+ mChannels.size() + ", new size " + mChannels.size());
+ for (Preference p : mChannels) {
+ getPreferenceScreen().removePreference(p);
+ }
+ }
+ if (mChannelGroup.getChannels().isEmpty()) {
+ Preference empty = new Preference(getPrefContext());
+ empty.setTitle(R.string.no_channels);
+ empty.setEnabled(false);
+ getPreferenceScreen().addPreference(empty);
+ mChannels.add(empty);
+
+ } else {
+ final List channels = mChannelGroup.getChannels();
+ Collections.sort(channels, mChannelComparator);
+ for (NotificationChannel channel : channels) {
+ mChannels.add(populateSingleChannelPrefs(
+ getPreferenceScreen(), channel, getImportanceSummary(channel)));
+ }
+
+ int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
+ if (deletedChannelCount > 0) {
+ mDeletedChannels = new FooterPreference(getPrefContext());
+ mDeletedChannels.setSelectable(false);
+ mDeletedChannels.setTitle(getResources().getQuantityString(
+ R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
+ mDeletedChannels.setEnabled(false);
+ mDeletedChannels.setKey(KEY_DELETED);
+ mDeletedChannels.setOrder(ORDER_LAST);
+ getPreferenceScreen().addPreference(mDeletedChannels);
+ mChannels.add(mDeletedChannels);
+ }
+ }
+
+ updateDependents(mAppRow.banned);
+ }
+
+ private void addHeaderPref() {
+ ArrayMap rows = new ArrayMap<>();
+ rows.put(mAppRow.pkg, mAppRow);
+ collectConfigActivities(rows);
+ final Activity activity = getActivity();
+ mHeaderPref = EntityHeaderController
+ .newInstance(activity, this /* fragment */, null /* header */)
+ .setRecyclerView(getListView(), getLifecycle());
+ final Preference pref = mHeaderPref
+ .setIcon(mAppRow.icon)
+ .setLabel(mChannelGroup.getName())
+ .setSummary(mAppRow.label)
+ .setPackageName(mAppRow.pkg)
+ .setUid(mAppRow.uid)
+ .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
+ EntityHeaderController.ActionType.ACTION_NONE)
+ .setHasAppInfoLink(true)
+ .done(activity, getPrefContext());
+ getPreferenceScreen().addPreference(pref);
+ }
+
+ private void addFooterPref() {
+ if (!TextUtils.isEmpty(mChannelGroup.getDescription())) {
+ FooterPreference descPref = new FooterPreference(getPrefContext());
+ descPref.setOrder(ORDER_LAST);
+ descPref.setSelectable(false);
+ descPref.setTitle(mChannelGroup.getDescription());
+ getPreferenceScreen().addPreference(descPref);
+ mChannels.add(descPref);
+ }
+ }
+
+ private void setupBlock() {
+ View switchBarContainer = LayoutInflater.from(
+ getPrefContext()).inflate(R.layout.styled_switch_bar, null);
+ mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
+ mSwitchBar.show();
+ mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
+ mSwitchBar.setChecked(!mChannelGroup.isBlocked());
+ mSwitchBar.addOnSwitchChangeListener((switchView, isChecked) -> {
+ mChannelGroup.setBlocked(!isChecked);
+ mBackend.updateChannelGroup(mPkg, mUid, mChannelGroup);
+ updateDependents(!isChecked);
+ });
+
+ mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
+ mBlockBar.setOrder(ORDER_FIRST);
+ mBlockBar.setKey(KEY_BLOCK);
+ getPreferenceScreen().addPreference(mBlockBar);
+
+ if (!isChannelGroupBlockable(mChannelGroup)) {
+ setVisible(mBlockBar, false);
+ }
+
+ setupBlockDesc(R.string.channel_group_notifications_off_desc);
+ }
+
+ protected void updateDependents(boolean banned) {
+ for (Preference channel : mChannels) {
+ setVisible(channel, !banned);
+ }
+ if (mAppLink != null) {
+ setVisible(mAppLink, !banned);
+ }
+ setVisible(mBlockBar, isChannelGroupBlockable(mChannelGroup));
+ setVisible(mBlockedDesc, mAppRow.banned || mChannelGroup.isBlocked());
+ }
+}
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 2f95dd29f38..41f98014535 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.AsyncTask;
import android.provider.Settings;
import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
@@ -57,6 +58,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
private static final String KEY_VIBRATE = "vibrate";
private static final String KEY_RINGTONE = "ringtone";
private static final String KEY_IMPORTANCE = "importance";
+ private static final String KEY_ADVANCED = "advanced";
private Preference mImportance;
private RestrictedSwitchPreference mLights;
@@ -65,6 +67,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
private FooterPreference mFooter;
private NotificationChannelGroup mChannelGroup;
private EntityHeaderController mHeaderPref;
+ private PreferenceGroup mAdvanced;
@Override
public int getMetricsCategory() {
@@ -96,24 +99,10 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
populateUpgradedChannelPrefs();
if (mChannel.getGroup() != null) {
- // Go look up group name
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... unused) {
- if (mChannel.getGroup() != null) {
- mChannelGroup = mBackend.getGroup(mChannel.getGroup(), mPkg, mUid);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void unused) {
- if (getHost() == null || mChannelGroup == null) {
- return;
- }
- setChannelGroupLabel(mChannelGroup.getName());
- }
- }.execute();
+ mChannelGroup = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
+ if (mChannelGroup != null) {
+ setChannelGroupLabel(mChannelGroup.getName());
+ }
}
}
@@ -129,6 +118,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
setupVibrate();
setupRingtone();
setupImportance();
+ mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
}
private void addHeaderPref() {
@@ -272,7 +262,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
mBlockBar.setKey(KEY_BLOCK);
getPreferenceScreen().addPreference(mBlockBar);
- if (!isChannelBlockable(mAppRow.systemApp, mChannel)) {
+ if (!isChannelBlockable(mChannel)) {
setVisible(mBlockBar, false);
}
@@ -373,6 +363,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
if (mShowLegacyChannelConfig) {
setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
} else {
+ setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(mLights, checkCanBeVisible(
NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 82e3a9e13ef..4de528e0a03 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -136,8 +136,7 @@ public class NotificationBackend {
}
}
-
- public NotificationChannelGroup getGroup(String groupId, String pkg, int uid) {
+ public NotificationChannelGroup getGroup(String pkg, int uid, String groupId) {
if (groupId == null) {
return null;
}
@@ -149,7 +148,19 @@ public class NotificationBackend {
}
}
- public ParceledListSlice getChannelGroups(String pkg, int uid) {
+ public NotificationChannelGroup getGroupWithChannels(String pkg, int uid, String groupId) {
+ if (groupId == null) {
+ return null;
+ }
+ try {
+ return sINM.getPopulatedNotificationChannelGroupForPackage(pkg, uid, groupId, true);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return null;
+ }
+ }
+
+ public ParceledListSlice getGroups(String pkg, int uid) {
try {
return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false);
} catch (Exception e) {
@@ -166,6 +177,15 @@ public class NotificationBackend {
}
}
+ public void updateChannelGroup(String pkg, int uid, NotificationChannelGroup group) {
+ try {
+ sINM.updateNotificationChannelGroupForPackage(pkg, uid, group);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ }
+
+
public int getDeletedChannelCount(String pkg, int uid) {
try {
return sINM.getDeletedChannelCount(pkg, uid);
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 38498829764..8c70a20e966 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -24,8 +24,10 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
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.applications.AppInfoBase;
import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.MasterSwitchPreference;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
@@ -33,6 +35,7 @@ import com.android.settingslib.widget.FooterPreference;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -51,8 +54,8 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
-import android.support.v7.preference.DropDownPreference;
import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -61,6 +64,7 @@ import android.widget.Toast;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
abstract public class NotificationSettingsBase extends SettingsPreferenceFragment {
@@ -106,6 +110,7 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
protected EnforcedAdmin mSuspendedAppsAdmin;
protected boolean mDndVisualEffectsSuppressed;
+ protected NotificationChannelGroup mChannelGroup;
protected NotificationChannel mChannel;
protected NotificationBackend.AppRow mAppRow;
protected boolean mShowLegacyChannelConfig = false;
@@ -185,6 +190,11 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
mChannel = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_ID)) ?
mBackend.getChannel(mPkg, mUid, args.getString(Settings.EXTRA_CHANNEL_ID)) : null;
+ mChannelGroup = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_GROUP_ID)) ?
+ mBackend.getGroupWithChannels(mPkg, mUid,
+ args.getString(Settings.EXTRA_CHANNEL_GROUP_ID))
+ : null;
+
mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
mContext, mPkg, mUserId);
NotificationManager.Policy policy = mNm.getNotificationPolicy();
@@ -249,6 +259,10 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
if (mChannel != null) {
row.settingsIntent.putExtra(Notification.EXTRA_CHANNEL_ID, mChannel.getId());
}
+ if (mChannelGroup != null) {
+ row.settingsIntent.putExtra(
+ Notification.EXTRA_CHANNEL_GROUP_ID, mChannelGroup.getId());
+ }
}
}
@@ -276,7 +290,7 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
protected void addAppLinkPref() {
if (mAppRow.settingsIntent != null && mAppLink == null) {
addPreferencesFromResource(R.xml.inapp_notification_settings);
- mAppLink = (Preference) findPreference(KEY_APP_LINK);
+ mAppLink = findPreference(KEY_APP_LINK);
mAppLink.setIntent(mAppRow.settingsIntent);
}
}
@@ -392,16 +406,56 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
}
protected void setupBlockDesc(int summaryResId) {
- mBlockedDesc = (FooterPreference) getPreferenceScreen().findPreference(
- KEY_BLOCKED_DESC);
mBlockedDesc = new FooterPreference(getPrefContext());
mBlockedDesc.setSelectable(false);
mBlockedDesc.setTitle(summaryResId);
mBlockedDesc.setEnabled(false);
mBlockedDesc.setOrder(50);
+ mBlockedDesc.setKey(KEY_BLOCKED_DESC);
getPreferenceScreen().addPreference(mBlockedDesc);
}
+ protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
+ final NotificationChannel channel, String summary) {
+ MasterSwitchPreference channelPref = new MasterSwitchPreference(
+ getPrefContext());
+ channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null
+ && isChannelBlockable(channel)
+ && isChannelConfigurable(channel));
+ channelPref.setKey(channel.getId());
+ channelPref.setTitle(channel.getName());
+ channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
+ channelPref.setSummary(summary);
+ Bundle channelArgs = new Bundle();
+ channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+ channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+ channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
+ Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+ ChannelNotificationSettings.class.getName(),
+ channelArgs, null, R.string.notification_channel_title, null, false,
+ getMetricsCategory());
+ channelPref.setIntent(channelIntent);
+
+ channelPref.setOnPreferenceChangeListener(
+ new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference,
+ Object o) {
+ boolean value = (Boolean) o;
+ int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
+ channel.setImportance(importance);
+ channel.lockFields(
+ NotificationChannel.USER_LOCKED_IMPORTANCE);
+ channelPref.setSummary(summary);
+ mBackend.updateChannel(mPkg, mUid, channel);
+
+ return true;
+ }
+ });
+ parent.addPreference(channelPref);
+ return channelPref;
+ }
+
protected boolean checkCanBeVisible(int minImportanceVisible) {
int importance = mChannel.getImportance();
if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
@@ -410,6 +464,26 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return importance >= minImportanceVisible;
}
+ protected String getImportanceSummary(NotificationChannel channel) {
+ switch (channel.getImportance()) {
+ case NotificationManager.IMPORTANCE_UNSPECIFIED:
+ return getContext().getString(R.string.notification_importance_unspecified);
+ case NotificationManager.IMPORTANCE_NONE:
+ return getContext().getString(R.string.notification_toggle_off);
+ case NotificationManager.IMPORTANCE_MIN:
+ return getContext().getString(R.string.notification_importance_min);
+ case NotificationManager.IMPORTANCE_LOW:
+ return getContext().getString(R.string.notification_importance_low);
+ case NotificationManager.IMPORTANCE_DEFAULT:
+ return getContext().getString(R.string.notification_importance_default);
+ case NotificationManager.IMPORTANCE_HIGH:
+ case NotificationManager.IMPORTANCE_MAX:
+ default:
+ return getContext().getString(R.string.notification_importance_high);
+ }
+
+ }
+
private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
CharSequence entryValue, int keyguardNotificationFeatures) {
RestrictedLockUtils.EnforcedAdmin admin =
@@ -459,7 +533,7 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return !channel.getId().equals(mAppRow.lockedChannelId);
}
- protected boolean isChannelBlockable(boolean systemApp, NotificationChannel channel) {
+ protected boolean isChannelBlockable(NotificationChannel channel) {
if (!mAppRow.systemApp) {
return true;
}
@@ -468,6 +542,14 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
|| channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
}
+ protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
+ if (!mAppRow.systemApp) {
+ return true;
+ }
+
+ return group.isBlocked();
+ }
+
protected void startListeningToPackageRemove() {
if (mListeningToPackageRemove) {
return;
@@ -501,4 +583,12 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
}
}
};
+
+ protected Comparator mChannelComparator =
+ (left, right) -> {
+ if (left.isDeleted() != right.isDeleted()) {
+ return Boolean.compare(left.isDeleted(), right.isDeleted());
+ }
+ return left.getId().compareTo(right.getId());
+ };
}
diff --git a/src/com/android/settings/widget/EntityHeaderController.java b/src/com/android/settings/widget/EntityHeaderController.java
index 70b040de935..c38ad0297dc 100644
--- a/src/com/android/settings/widget/EntityHeaderController.java
+++ b/src/com/android/settings/widget/EntityHeaderController.java
@@ -336,6 +336,7 @@ public class EntityHeaderController {
final Intent intent = resolveIntent(
new Intent(Intent.ACTION_APPLICATION_PREFERENCES).setPackage(mPackageName));
if (intent == null) {
+ button.setImageDrawable(null);
button.setVisibility(View.GONE);
return;
}
@@ -348,6 +349,7 @@ public class EntityHeaderController {
mFragment.startActivity(intent);
}
});
+ button.setImageResource(R.drawable.ic_settings_24dp);
button.setVisibility(View.VISIBLE);
return;
}
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 695342e3be0..a08536a94d5 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -23,6 +23,7 @@ com.android.settings.inputmethod.UserDictionaryList
com.android.settings.deviceinfo.Status
com.android.settings.datausage.DataSaverSummary
com.android.settings.notification.ChannelNotificationSettings
+com.android.settings.notification.ChannelGroupNotificationSettings
com.android.settings.datausage.AppDataUsage
com.android.settings.accessibility.FontSizePreferenceFragmentForSetupWizard
com.android.settings.applications.ManageDomainUrls
diff --git a/tests/robotests/src/com/android/settings/applications/PhotosViewHolderControllerTest.java b/tests/robotests/src/com/android/settings/applications/PhotosViewHolderControllerTest.java
new file mode 100644
index 00000000000..7eacba2e285
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/PhotosViewHolderControllerTest.java
@@ -0,0 +1,88 @@
+package com.android.settings.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
+import android.view.LayoutInflater;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.deviceinfo.StorageVolumeProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PhotosViewHolderControllerTest {
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Fragment mFragment;
+
+ @Mock private StorageVolumeProvider mSvp;
+ @Mock private StorageStatsSource mSource;
+
+ private Context mContext;
+ private PhotosViewHolderController mController;
+ private VolumeInfo mVolume;
+ private AppViewHolder mHolder;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mVolume = new VolumeInfo("id", 0, null, "id");
+ mController =
+ new PhotosViewHolderController(
+ mContext, mSource, mVolume.fsUuid, new UserHandle(0));
+
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mHolder = AppViewHolder.createOrRecycle(inflater, null);
+ }
+
+ @Test
+ public void storageShouldBeZeroBytesIfQueriedBeforeStorageQueryFinishes() {
+ mController.setupView(mHolder);
+
+ assertThat(mHolder.summary.getText().toString()).isEqualTo("0.00B");
+ }
+
+ @Test
+ public void storageShouldRepresentStorageStatsQuery() throws Exception {
+ when(mSource.getExternalStorageStats(nullable(String.class), nullable(UserHandle.class)))
+ .thenReturn(new StorageStatsSource.ExternalStorageStats(1, 0, 1, 10, 0));
+
+ mController.queryStats();
+ mController.setupView(mHolder);
+
+ assertThat(mHolder.summary.getText().toString()).isEqualTo("11.00B");
+ }
+
+ @Test
+ public void clickingShouldIntentIntoFilesApp() {
+ mController.onClick(mFragment);
+
+ final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mFragment).startActivity(argumentCaptor.capture());
+ Intent intent = argumentCaptor.getValue();
+
+ assertThat(intent.getType()).isEqualTo("image/*");
+ assertThat(intent.getAction()).isEqualTo(android.content.Intent.ACTION_VIEW);
+ assertThat(intent.getBooleanExtra(Intent.EXTRA_FROM_STORAGE, false)).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/core/codeinspection/ClassScanner.java b/tests/robotests/src/com/android/settings/core/codeinspection/ClassScanner.java
index 09af870fdfd..bf57f406e3a 100644
--- a/tests/robotests/src/com/android/settings/core/codeinspection/ClassScanner.java
+++ b/tests/robotests/src/com/android/settings/core/codeinspection/ClassScanner.java
@@ -16,50 +16,47 @@
package com.android.settings.core.codeinspection;
-import java.io.File;
+import com.google.common.reflect.ClassPath;
+
import java.io.IOException;
-import java.net.JarURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLDecoder;
import java.util.ArrayList;
-import java.util.Enumeration;
import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Scans and builds all classes in current classloader.
*/
public class ClassScanner {
- private static final String CLASS_SUFFIX = ".class";
-
public List> getClassesForPackage(String packageName)
throws ClassNotFoundException {
final List> classes = new ArrayList<>();
try {
- final Enumeration resources = Thread.currentThread().getContextClassLoader()
- .getResources(packageName.replace('.', '/'));
- if (!resources.hasMoreElements()) {
- return classes;
- }
- URL url = resources.nextElement();
- while (url != null) {
- final URLConnection connection = url.openConnection();
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ ClassPath classPath = ClassPath.from(classLoader);
- if (connection instanceof JarURLConnection) {
- loadClassFromJar((JarURLConnection) connection, packageName,
- classes);
- } else {
- loadClassFromDirectory(new File(URLDecoder.decode(url.getPath(), "UTF-8")),
- packageName, classes);
- }
- if (resources.hasMoreElements()) {
- url = resources.nextElement();
- } else {
- break;
+ // Some anonymous classes don't return true when calling isAnonymousClass(), but they
+ // always seem to be nested anonymous classes like com.android.settings.Foo$1$2. In
+ // general we don't want any anonymous classes so we just filter these out by searching
+ // for $[0-9] in the name.
+ Pattern anonymousClassPattern = Pattern.compile(".*\\$\\d+.*");
+ Matcher anonymousClassMatcher = anonymousClassPattern.matcher("");
+
+ for (ClassPath.ClassInfo info : classPath.getAllClasses()) {
+ if (info.getPackageName().startsWith(packageName)) {
+ try {
+ Class clazz = classLoader.loadClass(info.getName());
+ if (clazz.isAnonymousClass() || anonymousClassMatcher.reset(
+ clazz.getName()).matches()) {
+ continue;
+ }
+ classes.add(clazz);
+ } catch (NoClassDefFoundError e) {
+ // do nothing. this class hasn't been found by the
+ // loader, and we don't care.
+ }
}
}
} catch (final IOException e) {
@@ -68,63 +65,4 @@ public class ClassScanner {
return classes;
}
- private void loadClassFromDirectory(File directory, String packageName, List> classes)
- throws ClassNotFoundException {
- if (directory.exists() && directory.isDirectory()) {
- final String[] files = directory.list();
-
- for (final String file : files) {
- if (file.endsWith(CLASS_SUFFIX)) {
- try {
- classes.add(Class.forName(
- packageName + '.' + file.substring(0, file.length() - 6),
- false /* init */,
- Thread.currentThread().getContextClassLoader()));
- } catch (NoClassDefFoundError e) {
- // do nothing. this class hasn't been found by the
- // loader, and we don't care.
- }
- } else {
- final File tmpDirectory = new File(directory, file);
- if (tmpDirectory.isDirectory()) {
- loadClassFromDirectory(tmpDirectory, packageName + "." + file, classes);
- }
- }
- }
- }
- }
-
- private void loadClassFromJar(JarURLConnection connection, String packageName,
- List> classes) throws ClassNotFoundException, IOException {
- final JarFile jarFile = connection.getJarFile();
- final Enumeration entries = jarFile.entries();
- String name;
- if (!entries.hasMoreElements()) {
- return;
- }
- JarEntry jarEntry = entries.nextElement();
- while (jarEntry != null) {
- name = jarEntry.getName();
-
- if (name.contains(CLASS_SUFFIX)) {
- name = name.substring(0, name.length() - CLASS_SUFFIX.length()).replace('/', '.');
-
- if (name.startsWith(packageName)) {
- try {
- classes.add(Class.forName(name,
- false /* init */,
- Thread.currentThread().getContextClassLoader()));
- } catch (NoClassDefFoundError e) {
- // do nothing. this class hasn't been found by the
- // loader, and we don't care.
- }
- }
- }
- if (entries.hasMoreElements()) {
- jarEntry = entries.nextElement();
- } else {
- break;
- }
- }
- }
}
diff --git a/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java b/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java
index faaf338d15b..126a346da02 100644
--- a/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java
+++ b/tests/robotests/src/com/android/settings/core/codeinspection/CodeInspectionTest.java
@@ -44,14 +44,12 @@ public class CodeInspectionTest {
@Before
public void setUp() throws Exception {
mClasses = new ClassScanner().getClassesForPackage(CodeInspector.PACKAGE_NAME);
- // Disabled temporarily - see b/64840107
- //assertThat(mClasses).isNotEmpty();
+ assertThat(mClasses).isNotEmpty();
}
@Test
public void runCodeInspections() {
- // Disabled temporarily - see b/64840107
- // new InstrumentableFragmentCodeInspector(mClasses).run();
- // new SearchIndexProviderCodeInspector(mClasses).run();
+ new InstrumentableFragmentCodeInspector(mClasses).run();
+ new SearchIndexProviderCodeInspector(mClasses).run();
}
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index c728a608e2d..d3b5cbbea29 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -16,6 +16,7 @@
package com.android.settings.deviceinfo.storage;
+import static com.android.settings.applications.ManageApplications.EXTRA_WORK_ONLY;
import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
@@ -118,9 +119,12 @@ public class StorageItemPreferenceControllerTest {
nullable(UserHandle.class));
Intent intent = argumentCaptor.getValue();
- assertThat(intent.getType()).isEqualTo("image/*");
- assertThat(intent.getAction()).isEqualTo(android.content.Intent.ACTION_VIEW);
- assertThat(intent.getBooleanExtra(Intent.EXTRA_FROM_STORAGE, false)).isTrue();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN);
+ assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
+ assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(ManageApplications.class.getName());
+ assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
+ .isEqualTo(R.string.storage_photos_videos);
}
@Test
@@ -168,6 +172,29 @@ public class StorageItemPreferenceControllerTest {
.isEqualTo(R.string.apps_storage);
}
+ @Test
+ public void testClickAppsForWork() {
+ mController = new StorageItemPreferenceController(mContext, mFragment, mVolume, mSvp, true);
+ mPreference.setKey("pref_other_apps");
+ mController.handlePreferenceTreeClick(mPreference);
+
+ final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mFragment.getActivity())
+ .startActivityAsUser(argumentCaptor.capture(), nullable(UserHandle.class));
+
+ Intent intent = argumentCaptor.getValue();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN);
+ assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
+ assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(ManageApplications.class.getName());
+ assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
+ .isEqualTo(R.string.apps_storage);
+ assertThat(
+ intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
+ .getBoolean(EXTRA_WORK_ONLY))
+ .isTrue();
+ }
+
@Test
public void handlePreferenceTreeClick_tappingAppsWhileUninitializedDoesntCrash() {
mController.setVolume(null);
diff --git a/tests/robotests/src/com/android/settings/widget/EntityHeaderControllerTest.java b/tests/robotests/src/com/android/settings/widget/EntityHeaderControllerTest.java
index fc6071e552a..9c6ee457c42 100644
--- a/tests/robotests/src/com/android/settings/widget/EntityHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/widget/EntityHeaderControllerTest.java
@@ -16,7 +16,6 @@
package com.android.settings.widget;
-
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
@@ -30,6 +29,7 @@ import android.os.UserHandle;
import android.support.v7.preference.Preference;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.ImageButton;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
@@ -148,8 +148,9 @@ public class EntityHeaderControllerTest {
EntityHeaderController.ActionType.ACTION_NONE);
mController.done(mActivity);
- assertThat(appLinks.findViewById(android.R.id.button1).getVisibility())
- .isEqualTo(View.VISIBLE);
+ final ImageButton button1 = appLinks.findViewById(android.R.id.button1);
+ assertThat(button1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(button1.getDrawable()).isNotNull();
assertThat(appLinks.findViewById(android.R.id.button2).getVisibility())
.isEqualTo(View.GONE);
try {
@@ -176,8 +177,9 @@ public class EntityHeaderControllerTest {
EntityHeaderController.ActionType.ACTION_NONE);
mController.done(mActivity);
- assertThat(appLinks.findViewById(android.R.id.button1).getVisibility())
- .isEqualTo(View.GONE);
+ final ImageButton button1 = appLinks.findViewById(android.R.id.button1);
+ assertThat(button1.getVisibility()).isEqualTo(View.GONE);
+ assertThat(button1.getDrawable()).isNull();
assertThat(appLinks.findViewById(android.R.id.button2).getVisibility())
.isEqualTo(View.GONE);
}
diff --git a/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
index 22e98c71e5e..16a0b4324ad 100644
--- a/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
+++ b/tests/unit/src/com/android/settings/notification/AppNotificationSettingsTest.java
@@ -16,7 +16,29 @@
package com.android.settings.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import android.support.test.espresso.intent.Intents;
+
+import static android.support.test.espresso.intent.Intents.intended;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.fail;
+
import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
@@ -29,12 +51,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.hamcrest.Matchers.allOf;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AppNotificationSettingsTest {
@@ -42,10 +58,29 @@ public class AppNotificationSettingsTest {
private Context mTargetContext;
private Instrumentation mInstrumentation;
+ NotificationManager mNm;
+ private NotificationChannelGroup mGroup1;
+ private NotificationChannel mGroup1Channel1;
+ private NotificationChannel mGroup1Channel2;
+ private NotificationChannelGroup mGroup2;
+ private NotificationChannel mGroup2Channel1;
+ private NotificationChannel mUngroupedChannel;
+
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mTargetContext = mInstrumentation.getTargetContext();
+ mNm = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ mGroup1 = new NotificationChannelGroup(this.getClass().getName() + "1", "group1");
+ mGroup2 = new NotificationChannelGroup(this.getClass().getName() + "2", "group2");
+ mNm.createNotificationChannelGroup(mGroup1);
+ mNm.createNotificationChannelGroup(mGroup2);
+
+ mGroup1Channel1 = createChannel(mGroup1, this.getClass().getName()+ "c1-1");
+ mGroup1Channel2 = createChannel(mGroup1, this.getClass().getName()+ "c1-2");
+ mGroup2Channel1 = createChannel(mGroup2, this.getClass().getName()+ "c2-1");
+ mUngroupedChannel = createChannel(null, this.getClass().getName()+ "c");
}
@Test
@@ -60,4 +95,72 @@ public class AppNotificationSettingsTest {
.check(doesNotExist());
}
+ @Test
+ public void launchNotificationSetting_showGroupsWithMultipleChannels() {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+ mInstrumentation.startActivitySync(intent);
+ onView(allOf(withText(mGroup1.getName().toString()))).check(
+ matches(isDisplayed()));
+ try {
+ onView(allOf(withText(mGroup1Channel1.getName().toString())))
+ .check(matches(isDisplayed()));
+ fail("Channel erroneously appearing");
+ } catch (Exception e) {
+ // expected
+ }
+ // links to group page
+ Intents.init();
+ onView(allOf(withText(mGroup1.getName().toString()))).perform(click());
+ intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT,
+ ChannelGroupNotificationSettings.class.getName())));
+ Intents.release();
+ }
+
+ @Test
+ public void launchNotificationSetting_showUngroupedChannels() {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+ mInstrumentation.startActivitySync(intent);
+ onView(allOf(withText(mUngroupedChannel.getName().toString())))
+ .check(matches(isDisplayed()));
+ // links directly to channel page
+ Intents.init();
+ onView(allOf(withText(mUngroupedChannel.getName().toString()))).perform(click());
+ intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT, ChannelNotificationSettings.class.getName())));
+ Intents.release();
+ }
+
+ @Test
+ public void launchNotificationSetting_showGroupsWithOneChannel() {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName());
+ mInstrumentation.startActivitySync(intent);
+
+ onView(allOf(withText(mGroup2Channel1.getName().toString())))
+ .check(matches(isDisplayed()));
+ try {
+ onView(allOf(withText(mGroup2.getName().toString()))).check(
+ matches(isDisplayed()));
+ fail("Group erroneously appearing");
+ } catch (Exception e) {
+ // expected
+ }
+
+ // links directly to channel page
+ Intents.init();
+ onView(allOf(withText(mGroup2Channel1.getName().toString()))).perform(click());
+ intended(allOf(hasExtra(EXTRA_SHOW_FRAGMENT, ChannelNotificationSettings.class.getName())));
+ Intents.release();
+ }
+
+ private NotificationChannel createChannel(NotificationChannelGroup group,
+ String id) {
+ NotificationChannel channel = new NotificationChannel(id, id, IMPORTANCE_DEFAULT);
+ if (group != null) {
+ channel.setGroup(group.getId());
+ }
+ mNm.createNotificationChannel(channel);
+ return channel;
+ }
}
diff --git a/tests/unit/src/com/android/settings/notification/ChannelGroupNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/ChannelGroupNotificationSettingsTest.java
new file mode 100644
index 00000000000..ce2c408fa50
--- /dev/null
+++ b/tests/unit/src/com/android/settings/notification/ChannelGroupNotificationSettingsTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.fail;
+
+import android.app.INotificationManager;
+import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ChannelGroupNotificationSettingsTest {
+
+ private Context mTargetContext;
+ private Instrumentation mInstrumentation;
+ private NotificationManager mNm;
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mTargetContext = mInstrumentation.getTargetContext();
+ mNm = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ @Test
+ public void launchNotificationSetting_displaysChannels() {
+ NotificationChannelGroup group =
+ new NotificationChannelGroup(this.getClass().getName(), this.getClass().getName());
+ group.setDescription("description");
+ NotificationChannel channel = new NotificationChannel(this.getClass().getName(),
+ "channel" + this.getClass().getName(), IMPORTANCE_MIN);
+ channel.setGroup(this.getClass().getName());
+ NotificationChannel channel2 = new NotificationChannel("2"+this.getClass().getName(),
+ "2channel" + this.getClass().getName(), IMPORTANCE_MIN);
+ channel2.setGroup(this.getClass().getName());
+
+ mNm.createNotificationChannelGroup(group);
+ mNm.createNotificationChannel(channel);
+ mNm.createNotificationChannel(channel2);
+
+ final Intent intent = new Intent(Settings.ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+ .putExtra(Settings.EXTRA_CHANNEL_GROUP_ID, group.getId());
+
+ mInstrumentation.startActivitySync(intent);
+
+ onView(allOf(withText(group.getName().toString()))).check(matches(isDisplayed()));
+ onView(allOf(withText(channel.getName().toString()))).check(
+ matches(isDisplayed()));
+ onView(allOf(withText(group.getDescription().toString()))).check(
+ matches(isDisplayed()));
+ onView(allOf(withText(channel2.getName().toString()))).check(
+ matches(isDisplayed()));
+ try {
+ onView(allOf(withText("Android is blocking this group of notifications from"
+ + " appearing on this device"))).check(matches(isDisplayed()));
+ fail("Blocking footer erroneously appearing");
+ } catch (Exception e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void launchNotificationSettings_blockedGroup() throws Exception {
+ NotificationChannelGroup blocked =
+ new NotificationChannelGroup("blocked", "blocked");
+ NotificationChannel channel =
+ new NotificationChannel("channel", "channel", IMPORTANCE_HIGH);
+ channel.setGroup(blocked.getId());
+ mNm.createNotificationChannelGroup(blocked);
+ mNm.createNotificationChannel(channel);
+
+ INotificationManager sINM = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ blocked.setBlocked(true);
+ sINM.updateNotificationChannelGroupForPackage(
+ mTargetContext.getPackageName(), Process.myUid(), blocked);
+
+ final Intent intent = new Intent(Settings.ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+ .putExtra(Settings.EXTRA_CHANNEL_GROUP_ID, blocked.getId());
+ mInstrumentation.startActivitySync(intent);
+
+ onView(allOf(withText("Off"), isDisplayed())).check(matches(isDisplayed()));
+ onView(allOf(withText("Android is blocking this group of notifications from"
+ + " appearing on this device"))).check(matches(isDisplayed()));
+
+ try {
+ onView(allOf(withText(channel.getName().toString()))).check(matches(isDisplayed()));
+ fail("settings appearing for blocked group");
+ } catch (Exception e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java b/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
new file mode 100644
index 00000000000..1244dcda4f5
--- /dev/null
+++ b/tests/unit/src/com/android/settings/notification/ChannelNotificationSettingsTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.fail;
+
+import android.app.INotificationManager;
+import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ChannelNotificationSettingsTest {
+
+ private Context mTargetContext;
+ private Instrumentation mInstrumentation;
+ private NotificationChannel mNotificationChannel;
+ private NotificationManager mNm;
+
+ @Before
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mTargetContext = mInstrumentation.getTargetContext();
+
+ mNm = (NotificationManager) mTargetContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationChannel = new NotificationChannel(this.getClass().getName(),
+ this.getClass().getName(), IMPORTANCE_MIN);
+ mNm.createNotificationChannel(mNotificationChannel);
+ }
+
+ @Test
+ public void launchNotificationSetting_shouldNotCrash() {
+ final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+ .putExtra(Settings.EXTRA_CHANNEL_ID, mNotificationChannel.getId());
+ mInstrumentation.startActivitySync(intent);
+
+ onView(allOf(withText(mNotificationChannel.getName().toString()))).check(
+ matches(isDisplayed()));
+ }
+
+ @Test
+ public void launchNotificationSettings_blockedChannel() throws Exception {
+ NotificationChannel blocked =
+ new NotificationChannel("blocked", "blocked", IMPORTANCE_NONE);
+ mNm.createNotificationChannel(blocked);
+
+ INotificationManager sINM = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ blocked.setImportance(IMPORTANCE_NONE);
+ sINM.updateNotificationChannelForPackage(
+ mTargetContext.getPackageName(), Process.myUid(), blocked);
+
+ final Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, mTargetContext.getPackageName())
+ .putExtra(Settings.EXTRA_CHANNEL_ID, blocked.getId());
+ mInstrumentation.startActivitySync(intent);
+
+ onView(allOf(withText("Off"), isDisplayed())).check(matches(isDisplayed()));
+ onView(allOf(withText("Android is blocking this category of notifications from"
+ + " appearing on this device"))).check(matches(isDisplayed()));
+
+ try {
+ onView(allOf(withText("On the lock screen"))).check(matches(isDisplayed()));
+ fail("settings appearing for blocked channel");
+ } catch (Exception e) {
+ // expected
+ }
+ }
+}