release-request-b4448e78-a682-4bc1-b691-b70e74fef19f-for-git_pi-release-4314474 snap-temp-L95500000099150132

Change-Id: Ifeaa9bea2c020dbb0cd9bac3f0151a0c92ebb2df
This commit is contained in:
android-build-team Robot
2017-09-03 08:08:04 +00:00
31 changed files with 1100 additions and 265 deletions

View File

@@ -2700,6 +2700,22 @@
android:value="com.android.settings.notification.AppNotificationSettings" />
</activity>
<!-- Show channel group-level notification settings (group passed in as extras) -->
<activity android:name="Settings$ChannelGroupNotificationSettingsActivity"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.notification.ChannelGroupNotificationSettings" />
</activity>
<!-- Show channel-level notification settings (channel passed in as extras) -->
<activity android:name="Settings$ChannelNotificationSettingsActivity"
android:exported="true">

View File

@@ -21,10 +21,5 @@
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3 10v4c0 .55 .45 1 1 1h3l3.29 3.29c.63 .63 1.71 .18
1.71-.71V6.41c0-.89-1.08-1.34-1.71-.71L7 9H4c-.55 0-1 .45-1 1zm13.5 2A4.5 4.5 0
0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 4.46v.19c0 .38 .25 .71 .61
.85C17.18 6.54 19 9.06 19 12s-1.82 5.46-4.39 6.5c-.36 .14 -.61 .47 -.61 .85
v.19c0 .63 .63 1.08 1.22 .86 a8.995 8.995 0 0 0 0-16.8c-.59-.23-1.22 .23 -1.22
.86 z"/>
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"/>
</vector>

View File

@@ -6836,11 +6836,14 @@
<!-- [CHAR LIMIT=100] Notification channel title -->
<string name="notification_channel_title">Notification category</string>
<!-- [CHAR LIMIT=200] Notification channel group title -->
<string name="notification_group_title">Notification category group</string>
<!-- [CHAR LIMIT=100] Notification importance screen title -->
<string name="notification_importance_title">Importance</string>
<!-- [CHAR LIMIT=100] Notification Importance: unspecified importance level description -->
<string name="notification_importance_unspecified">Let the app decide</string>
<!-- [CHAR LIMIT=100 BACKUP_MESSAGE_ID=1820188704793497324] Notification Importance: unspecified importance level description -->
<string name="notification_importance_unspecified">Allow sound</string>
<!-- [CHAR LIMIT=100] Notification Importance: blocked importance level description -->
<string name="notification_importance_blocked">Never show notifications</string>
@@ -7003,12 +7006,21 @@
<!-- [CHAR LIMIT=NONE] Text appearing when channel notifications are off -->
<string name="channel_notifications_off_desc">Android is blocking this category of notifications from appearing on this device</string>
<!-- [CHAR LIMIT=NONE] Text appearing when channel group notifications are off -->
<string name="channel_group_notifications_off_desc">Android is blocking this group of notifications from appearing on this device</string>
<!-- [CHAR LIMIT=NONE] App notification settings: channels title -->
<string name="notification_channels">Categories</string>
<!-- [CHAR LIMIT=NONE] App notification settings: non-grouped-channels title -->
<string name="notification_channels_other">Other</string>
<!-- [CHAR LIMIT=45] App notification settings, group summary-->
<plurals name="notification_group_summary">
<item quantity="one"><xliff:g id="count" example="1">%d</xliff:g> category</item>
<item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> categories</item>
</plurals>
<!-- [CHAR LIMIT=NONE] App notification settings: no channels -->
<string name="no_channels">This app has not posted any notifications</string>

View File

@@ -38,6 +38,7 @@
settings:useAdditionalSummary="true" />
<PreferenceCategory
android:key="advanced"
android:title="@string/advanced_apps">
<!-- Visibility Override -->

View File

@@ -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());

View File

@@ -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 */ }

View File

@@ -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.

View File

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

View File

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

View File

@@ -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();
}
}

View File

@@ -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(),

View File

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

View File

@@ -101,8 +101,13 @@ public class StorageProfileFragment extends DashboardFragment
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
final List<AbstractPreferenceController> 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;
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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<Drawable> 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;
}
}

View File

@@ -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<Void, Void, Void>() {
@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<NotificationChannel> 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<NotificationChannel> 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<NotificationChannel> mChannelComparator =
new Comparator<NotificationChannel>() {
@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<NotificationChannelGroup> mChannelGroupComparator =
new Comparator<NotificationChannelGroup>() {

View File

@@ -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<Preference> 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<NotificationChannel> 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<String, NotificationBackend.AppRow> 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());
}
}

View File

@@ -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<Void, Void, Void>() {
@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());

View File

@@ -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<NotificationChannelGroup> 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<NotificationChannelGroup> 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);

View File

@@ -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<NotificationChannel> mChannelComparator =
(left, right) -> {
if (left.isDeleted() != right.isDeleted()) {
return Boolean.compare(left.isDeleted(), right.isDeleted());
}
return left.getId().compareTo(right.getId());
};
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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<Intent> 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();
}
}

View File

@@ -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<Class<?>> getClassesForPackage(String packageName)
throws ClassNotFoundException {
final List<Class<?>> classes = new ArrayList<>();
try {
final Enumeration<URL> 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<Class<?>> 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<Class<?>> classes) throws ClassNotFoundException, IOException {
final JarFile jarFile = connection.getJarFile();
final Enumeration<JarEntry> 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;
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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<Intent> 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);

View File

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

View File

@@ -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;
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}