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
This commit is contained in:
Julia Reynolds
2021-03-10 21:38:49 -05:00
parent 61aa289dac
commit efe26e2d44
10 changed files with 304 additions and 53 deletions

View File

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

View File

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

View File

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

View File

@@ -68,12 +68,16 @@ public class ChannelNotificationSettings extends NotificationSettings {
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 @@ public class ChannelNotificationSettings extends NotificationSettings {
controller.displayPreference(getPreferenceScreen());
}
updatePreferenceStates();
animatePanel();
}
@Override

View File

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

View File

@@ -93,6 +93,7 @@ public class ConversationHeaderPreferenceController extends NotificationPreferen
.done(activity, mContext);
pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE);
pref.findViewById(R.id.entity_header).setBackground(null);
}
}

View File

@@ -49,10 +49,11 @@ public class ConversationNotificationSettings extends NotificationSettings {
for (NotificationPreferenceController controller : mControllers) {
controller.onResume(mAppRow, mChannel, mChannelGroup, mConversationDrawable,
mConversationInfo, mSuspendedAppsAdmin, null);
mConversationInfo, mSuspendedAppsAdmin, mPreferenceFilter);
controller.displayPreference(getPreferenceScreen());
}
updatePreferenceStates();
animatePanel();
}
@Override

View File

@@ -92,6 +92,7 @@ public class HeaderPreferenceController extends NotificationPreferenceController
.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);
}
}

View File

@@ -18,6 +18,9 @@ package com.android.settings.notification.app;
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.os.UserHandle;
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 @@ abstract public class NotificationSettings extends DashboardFragment {
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 @@ abstract public class NotificationSettings extends DashboardFragment {
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