Launch channel settings as half sheet
If opening app has requested only a subset of fields
Test: manual - launch from an app, filtered and unfiltered, for
normal channels and conversations
Bug: 177246841
Change-Id: Ifd70478101d1ea1340d2ecc55033fab55e65ca92
Change-Id: I5194b959c82b2cfa7990c84285aaf69464cff3a8
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 86d7992..8191042 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2803,8 +2803,13 @@
</activity>
<!-- Show channel-level notification settings (channel passed in as extras) -->
- <activity android:name="Settings$ChannelNotificationSettingsActivity"
+ <activity android:name=".notification.app.ChannelPanelActivity"
android:label="@string/notification_channel_title"
+ android:theme="@style/Theme.Panel"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
+ android:noHistory="true"
+ android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.CHANNEL_NOTIFICATION_SETTINGS" />
diff --git a/res/layout/notification_channel_panel.xml b/res/layout/notification_channel_panel.xml
new file mode 100644
index 0000000..49cd95c
--- /dev/null
+++ b/res/layout/notification_channel_panel.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main_content"
+ android:layout_height="@dimen/output_switcher_slice_max_height"
+ android:background="@drawable/settings_panel_background"
+ android:orientation="vertical"
+ android:layout_width="match_parent">
+
+ <FrameLayout
+ android:id="@android:id/list_container"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:layout_width="match_parent"/>
+
+ <View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/footer_divider"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/horizontal_divider_height"
+ android:background="?android:attr/dividerHorizontal"/>
+
+ <LinearLayout
+ android:id="@+id/footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <Button
+ android:id="@+id/see_more"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="12dp"
+ android:text="@string/see_more"/>
+
+ <Space
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+
+ <Button
+ android:id="@+id/done"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="12dp"
+ android:text="@string/done"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml
index 707cae9..95f7944 100644
--- a/res/xml/channel_notification_settings.xml
+++ b/res/xml/channel_notification_settings.xml
@@ -65,56 +65,56 @@
android:summary="@string/promote_conversation_summary"
settings:allowDividerAbove="true"/>
- <!-- Default ringtone -->
- <com.android.settings.notification.app.NotificationSoundPreference
- android:key="ringtone"
- android:title="@string/notification_channel_sound_title"
- android:dialogTitle="@string/notification_channel_sound_title"
- android:order="11"
- android:showSilent="true"
- android:showDefault="true"/>
+ <!-- Default ringtone -->
+ <com.android.settings.notification.app.NotificationSoundPreference
+ android:key="ringtone"
+ android:title="@string/notification_channel_sound_title"
+ android:dialogTitle="@string/notification_channel_sound_title"
+ android:order="11"
+ android:showSilent="true"
+ android:showDefault="true"/>
- <!-- Vibration -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="vibrate"
- android:order="12"
- android:title="@string/notification_vibrate_title"
- settings:useAdditionalSummary="true" />
+ <!-- Vibration -->
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="vibrate"
+ android:order="12"
+ android:title="@string/notification_vibrate_title"
+ settings:useAdditionalSummary="true" />
- <!-- Visibility Override -->
- <com.android.settings.RestrictedListPreference
- android:key="visibility_override"
- android:order="13"
- android:title="@string/app_notification_visibility_override_title"/>
+ <!-- Visibility Override -->
+ <com.android.settings.RestrictedListPreference
+ android:key="visibility_override"
+ android:order="13"
+ android:title="@string/app_notification_visibility_override_title"/>
- <!-- Lights -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="lights"
- android:order="14"
- android:title="@string/notification_show_lights_title"
- settings:useAdditionalSummary="true"/>
+ <!-- Lights -->
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="lights"
+ android:order="14"
+ android:title="@string/notification_show_lights_title"
+ settings:useAdditionalSummary="true"/>
- <!-- Show badge -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="badge"
- android:order="15"
- android:title="@string/notification_channel_badge_title"
- settings:useAdditionalSummary="true"
- settings:restrictedSwitchSummary="@string/enabled_by_admin"/>
+ <!-- Show badge -->
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="badge"
+ android:order="15"
+ android:title="@string/notification_channel_badge_title"
+ settings:useAdditionalSummary="true"
+ settings:restrictedSwitchSummary="@string/enabled_by_admin"/>
- <!-- Bypass DND -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="bypass_dnd"
- android:order="17"
- android:title="@string/app_notification_override_dnd_title"
- android:summary="@string/app_notification_override_dnd_summary"
- settings:useAdditionalSummary="true"/>
+ <!-- Bypass DND -->
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="bypass_dnd"
+ android:order="17"
+ android:title="@string/app_notification_override_dnd_title"
+ android:summary="@string/app_notification_override_dnd_summary"
+ settings:useAdditionalSummary="true"/>
- <Preference
- android:key="app_link"
- android:order="18"
- android:title="@string/app_settings_link"
- settings:allowDividerAbove="true"/>
+ <Preference
+ android:key="app_link"
+ android:order="18"
+ android:title="@string/app_settings_link"
+ settings:allowDividerAbove="true"/>
<com.android.settings.notification.app.NotificationFooterPreference
android:key="desc"
diff --git a/res/xml/conversation_notification_settings.xml b/res/xml/conversation_notification_settings.xml
index 65fdee5..ea555c2 100644
--- a/res/xml/conversation_notification_settings.xml
+++ b/res/xml/conversation_notification_settings.xml
@@ -62,10 +62,6 @@
android:key="block_desc"
settings:allowDividerAbove="false"/>
- <PreferenceCategory
- android:key="channel_advanced"
- android:order="50"
- settings:initialExpandedChildrenCount="0">
<!-- peeking -->
<com.android.settingslib.RestrictedSwitchPreference
@@ -107,6 +103,5 @@
android:icon="@drawable/ic_volume_ringer_vibrate"
android:title="@string/notification_vibrate_title"
settings:useAdditionalSummary="true" />
- </PreferenceCategory>
</PreferenceScreen>
diff --git a/src/com/android/settings/notification/app/ChannelNotificationSettings.java b/src/com/android/settings/notification/app/ChannelNotificationSettings.java
index bdea30d..296863c 100644
--- a/src/com/android/settings/notification/app/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/app/ChannelNotificationSettings.java
@@ -68,12 +68,16 @@
if (mChannel != null && !TextUtils.isEmpty(mChannel.getConversationId())
&& !mChannel.isDemoted()) {
- startActivity(new SubSettingLauncher(mContext)
+ Intent intent = new SubSettingLauncher(mContext)
.setDestination(ConversationNotificationSettings.class.getName())
.setArguments(getArguments())
.setExtras(getIntent() != null ? getIntent().getExtras(): null)
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_TOPIC_NOTIFICATION)
- .toIntent());
+ .toIntent();
+ if (mPreferenceFilter != null) {
+ intent.setClass(mContext, ChannelPanelActivity.class);
+ }
+ startActivity(intent);
finish();
return;
}
@@ -84,6 +88,7 @@
controller.displayPreference(getPreferenceScreen());
}
updatePreferenceStates();
+ animatePanel();
}
@Override
diff --git a/src/com/android/settings/notification/app/ChannelPanelActivity.java b/src/com/android/settings/notification/app/ChannelPanelActivity.java
new file mode 100644
index 0000000..3ba118e
--- /dev/null
+++ b/src/com/android/settings/notification/app/ChannelPanelActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.app;
+
+import android.app.settings.SettingsEnums;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.core.HideNonSystemOverlayMixin;
+import com.android.settings.core.SubSettingLauncher;
+
+/**
+ * Dialog Activity to host channel settings
+ */
+public class ChannelPanelActivity extends FragmentActivity {
+
+ private static final String TAG = "ChannelPanelActivity";
+
+ final Bundle mBundle = new Bundle();
+ NotificationSettings mPanelFragment;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!getIntent().hasExtra(Settings.EXTRA_CHANNEL_FILTER_LIST)) {
+ launchFullSettings();
+ }
+
+ getApplicationContext().getTheme().rebase();
+ createOrUpdatePanel();
+ getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ createOrUpdatePanel();
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ private void launchFullSettings() {
+ Bundle extras = getIntent().getExtras();
+ extras.remove(Settings.EXTRA_CHANNEL_FILTER_LIST);
+ startActivity(new SubSettingLauncher(this)
+ .setDestination(ChannelNotificationSettings.class.getName())
+ .setExtras(extras)
+ .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_TOPIC_NOTIFICATION)
+ .toIntent());
+ finish();
+ }
+
+ private void createOrUpdatePanel() {
+ final Intent callingIntent = getIntent();
+ if (callingIntent == null) {
+ Log.e(TAG, "Null intent, closing Panel Activity");
+ finish();
+ return;
+ }
+
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ setContentView(R.layout.notification_channel_panel);
+
+ // Move the window to the bottom of screen, and make it take up the entire screen width.
+ final Window window = getWindow();
+ window.setGravity(Gravity.BOTTOM);
+ window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.WRAP_CONTENT);
+
+ findViewById(R.id.done).setOnClickListener(v -> finish());
+ findViewById(R.id.see_more).setOnClickListener(v -> launchFullSettings());
+
+ mPanelFragment = callingIntent.hasExtra(Settings.EXTRA_CONVERSATION_ID)
+ ? new ConversationNotificationSettings()
+ : new ChannelNotificationSettings();
+ mPanelFragment.setArguments(new Bundle(mBundle));
+ fragmentManager.beginTransaction().replace(
+ android.R.id.list_container, mPanelFragment).commit();
+ }
+}
diff --git a/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java b/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java
index 313b387..73659b8 100644
--- a/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java
@@ -93,6 +93,7 @@
.done(activity, mContext);
pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE);
+ pref.findViewById(R.id.entity_header).setBackground(null);
}
}
diff --git a/src/com/android/settings/notification/app/ConversationNotificationSettings.java b/src/com/android/settings/notification/app/ConversationNotificationSettings.java
index 819ba83..d712c84 100644
--- a/src/com/android/settings/notification/app/ConversationNotificationSettings.java
+++ b/src/com/android/settings/notification/app/ConversationNotificationSettings.java
@@ -49,10 +49,11 @@
for (NotificationPreferenceController controller : mControllers) {
controller.onResume(mAppRow, mChannel, mChannelGroup, mConversationDrawable,
- mConversationInfo, mSuspendedAppsAdmin, null);
+ mConversationInfo, mSuspendedAppsAdmin, mPreferenceFilter);
controller.displayPreference(getPreferenceScreen());
}
updatePreferenceStates();
+ animatePanel();
}
@Override
diff --git a/src/com/android/settings/notification/app/HeaderPreferenceController.java b/src/com/android/settings/notification/app/HeaderPreferenceController.java
index 6482943..4e9c039 100644
--- a/src/com/android/settings/notification/app/HeaderPreferenceController.java
+++ b/src/com/android/settings/notification/app/HeaderPreferenceController.java
@@ -92,6 +92,7 @@
.setRecyclerView(mFragment.getListView(), mFragment.getSettingsLifecycle())
.done(activity, mContext);
pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE);
+ pref.findViewById(R.id.entity_header).setBackground(null);
}
}
diff --git a/src/com/android/settings/notification/app/NotificationSettings.java b/src/com/android/settings/notification/app/NotificationSettings.java
index 44c735a..99f067d 100644
--- a/src/com/android/settings/notification/app/NotificationSettings.java
+++ b/src/com/android/settings/notification/app/NotificationSettings.java
@@ -18,6 +18,9 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -39,8 +42,13 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.DecelerateInterpolator;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
@@ -85,6 +93,20 @@
protected Intent mIntent;
protected Bundle mArgs;
+ private ViewGroup mLayoutView;
+ private static final int DURATION_ANIMATE_PANEL_EXPAND_MS = 250;
+
+ private final ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener =
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ animateIn();
+ if (mLayoutView != null) {
+ mLayoutView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ }
+ };
+
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -187,6 +209,50 @@
collectConfigActivities();
}
+ protected void animatePanel() {
+ if (mPreferenceFilter != null) {
+ mLayoutView = getActivity().findViewById(R.id.main_content);
+ mLayoutView.getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
+ }
+ }
+
+ /**
+ * Animate a Panel onto the screen.
+ * <p>
+ * Takes the entire panel and animates in from behind the navigation bar.
+ * <p>
+ * Relies on the Panel being having a fixed height to begin the animation.
+ */
+ private void animateIn() {
+ final AnimatorSet animatorSet = buildAnimatorSet(mLayoutView,
+ mLayoutView.getHeight() /* startY */, 0.0f /* endY */,
+ 0.0f /* startAlpha */, 1.0f /* endAlpha */,
+ DURATION_ANIMATE_PANEL_EXPAND_MS);
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setFloatValues(0.0f, 1.0f);
+ animatorSet.play(animator);
+ animatorSet.start();
+ }
+
+ /**
+ * Build an {@link AnimatorSet} to animate the Panel, {@param parentView} in or out of the
+ * screen, based on the positional parameters {@param startY}, {@param endY}, the parameters
+ * for alpha changes {@param startAlpha}, {@param endAlpha}, and the {@param duration} in
+ * milliseconds.
+ */
+ @NonNull
+ private static AnimatorSet buildAnimatorSet(@NonNull View targetView,
+ float startY, float endY,
+ float startAlpha, float endAlpha, int duration) {
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.setDuration(duration);
+ animatorSet.setInterpolator(new DecelerateInterpolator());
+ animatorSet.playTogether(
+ ObjectAnimator.ofFloat(targetView, View.TRANSLATION_Y, startY, endY),
+ ObjectAnimator.ofFloat(targetView, View.ALPHA, startAlpha, endAlpha));
+ return animatorSet;
+ }
+
private void loadPreferencesFilter() {
Intent intent = getActivity().getIntent();
mPreferenceFilter = intent != null