diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2018-10-16 16:36:02 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2018-10-19 18:09:40 +0000 |
commit | b2498b279095fbddb8d05031158638d79491bb0d (patch) | |
tree | 523d5296b7d31f52a90bcaf4595e3cf04c176c93 /src/com/android/launcher3/settings | |
parent | 785a379570f6350a4d1b51f93a98852d56569c98 (diff) | |
download | android_packages_apps_Trebuchet-b2498b279095fbddb8d05031158638d79491bb0d.tar.gz android_packages_apps_Trebuchet-b2498b279095fbddb8d05031158638d79491bb0d.tar.bz2 android_packages_apps_Trebuchet-b2498b279095fbddb8d05031158638d79491bb0d.zip |
Using support lib implementation for launcher preference
Bug: 117519297
Change-Id: Icea5e022a337436e48db9376fd441f805dc34e54
Diffstat (limited to 'src/com/android/launcher3/settings')
3 files changed, 515 insertions, 0 deletions
diff --git a/src/com/android/launcher3/settings/IconBadgingPreference.java b/src/com/android/launcher3/settings/IconBadgingPreference.java new file mode 100644 index 000000000..7c97b38d2 --- /dev/null +++ b/src/com/android/launcher3/settings/IconBadgingPreference.java @@ -0,0 +1,138 @@ +/* + * 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.launcher3.settings; + +import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY; +import static com.android.launcher3.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGS; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.provider.Settings; +import android.util.AttributeSet; +import android.view.View; + +import com.android.launcher3.R; +import com.android.launcher3.notification.NotificationListener; +import com.android.launcher3.util.SecureSettingsObserver; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * A {@link Preference} for indicating icon badging status. + * Also has utility methods for updating UI based on badging status changes. + */ +public class IconBadgingPreference extends Preference + implements SecureSettingsObserver.OnChangeListener { + + private boolean mWidgetFrameVisible = false; + + /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ + private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners"; + + public IconBadgingPreference( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public IconBadgingPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public IconBadgingPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public IconBadgingPreference(Context context) { + super(context); + } + + private void setWidgetFrameVisible(boolean isVisible) { + if (mWidgetFrameVisible != isVisible) { + mWidgetFrameVisible = isVisible; + notifyChanged(); + } + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + View widgetFrame = holder.findViewById(android.R.id.widget_frame); + if (widgetFrame != null) { + widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE); + } + } + + @Override + public void onSettingsChanged(boolean enabled) { + int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off; + + boolean serviceEnabled = true; + if (enabled) { + // Check if the listener is enabled or not. + String enabledListeners = Settings.Secure.getString( + getContext().getContentResolver(), NOTIFICATION_ENABLED_LISTENERS); + ComponentName myListener = + new ComponentName(getContext(), NotificationListener.class); + serviceEnabled = enabledListeners != null && + (enabledListeners.contains(myListener.flattenToString()) || + enabledListeners.contains(myListener.flattenToShortString())); + if (!serviceEnabled) { + summary = R.string.title_missing_notification_access; + } + } + setWidgetFrameVisible(!serviceEnabled); + setFragment(serviceEnabled ? null : NotificationAccessConfirmation.class.getName()); + setSummary(summary); + } + + public static class NotificationAccessConfirmation + extends DialogFragment implements DialogInterface.OnClickListener { + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Context context = getActivity(); + String msg = context.getString(R.string.msg_missing_notification_access, + context.getString(R.string.derived_app_name)); + return new AlertDialog.Builder(context) + .setTitle(R.string.title_missing_notification_access) + .setMessage(msg) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.title_change_settings, this) + .create(); + } + + @Override + public void onClick(DialogInterface dialogInterface, int i) { + ComponentName cn = new ComponentName(getActivity(), NotificationListener.class); + Bundle showFragmentArgs = new Bundle(); + showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString()); + + Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString()) + .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs); + getActivity().startActivity(intent); + } + } +} diff --git a/src/com/android/launcher3/settings/PreferenceHighlighter.java b/src/com/android/launcher3/settings/PreferenceHighlighter.java new file mode 100644 index 000000000..4ed4cf113 --- /dev/null +++ b/src/com/android/launcher3/settings/PreferenceHighlighter.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2018 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.launcher3.settings; + +import static androidx.core.graphics.ColorUtils.setAlphaComponent; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.Property; +import android.view.View; + +import com.android.launcher3.util.Themes; + +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.ItemDecoration; +import androidx.recyclerview.widget.RecyclerView.State; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; + +/** + * Utility class for highlighting a preference + */ +public class PreferenceHighlighter extends ItemDecoration implements Runnable { + + private static final Property<PreferenceHighlighter, Integer> HIGHLIGHT_COLOR = + new Property<PreferenceHighlighter, Integer>(Integer.TYPE, "highlightColor") { + + @Override + public Integer get(PreferenceHighlighter highlighter) { + return highlighter.mHighlightColor; + } + + @Override + public void set(PreferenceHighlighter highlighter, Integer value) { + highlighter.mHighlightColor = value; + highlighter.mRv.invalidateItemDecorations(); + } + }; + + private static final long HIGHLIGHT_DURATION = 15000L; + private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L; + private static final long HIGHLIGHT_FADE_IN_DURATION = 200L; + private static final int END_COLOR = setAlphaComponent(Color.WHITE, 0); + + private final Paint mPaint = new Paint(); + private final RecyclerView mRv; + private final int mIndex; + + private boolean mHighLightStarted = false; + private int mHighlightColor = END_COLOR; + + + public PreferenceHighlighter(RecyclerView rv, int index) { + mRv = rv; + mIndex = index; + } + + @Override + public void run() { + mRv.addItemDecoration(this); + mRv.smoothScrollToPosition(mIndex); + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, State state) { + ViewHolder holder = parent.findViewHolderForAdapterPosition(mIndex); + if (holder == null) { + return; + } + if (!mHighLightStarted && state.getRemainingScrollVertical() != 0) { + // Wait until scrolling stopped + return; + } + + if (!mHighLightStarted) { + // Start highlight + int colorTo = setAlphaComponent(Themes.getColorAccent(mRv.getContext()), 66); + ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR, colorTo); + anim.setDuration(HIGHLIGHT_FADE_IN_DURATION); + anim.setRepeatMode(ValueAnimator.REVERSE); + anim.setRepeatCount(4); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + removeHighlight(); + } + }); + anim.start(); + mHighLightStarted = true; + } + + View view = holder.itemView; + mPaint.setColor(mHighlightColor); + c.drawRect(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight(), mPaint); + } + + private void removeHighlight() { + ObjectAnimator anim = ObjectAnimator.ofArgb( + this, HIGHLIGHT_COLOR, mHighlightColor, END_COLOR); + anim.setDuration(HIGHLIGHT_FADE_OUT_DURATION); + anim.setStartDelay(HIGHLIGHT_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRv.removeItemDecoration(PreferenceHighlighter.this); + } + }); + anim.start(); + } +} diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java new file mode 100644 index 000000000..66420d079 --- /dev/null +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015 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.launcher3.settings; + +import static com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY; +import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; +import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue; +import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; + +import android.app.Activity; +import android.app.DialogFragment; +import android.app.Fragment; +import android.content.Intent; +import android.os.Bundle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import com.android.launcher3.LauncherFiles; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.graphics.IconShapeOverride; +import com.android.launcher3.util.SecureSettingsObserver; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragment; +import androidx.preference.PreferenceFragment.OnPreferenceStartFragmentCallback; +import androidx.preference.PreferenceFragment.OnPreferenceStartScreenCallback; +import androidx.preference.PreferenceGroup.PreferencePositionCallback; +import androidx.preference.PreferenceScreen; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Settings activity for Launcher. Currently implements the following setting: Allow rotation + */ +public class SettingsActivity extends Activity + implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback { + + private static final String FLAGS_PREFERENCE_KEY = "flag_toggler"; + + private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging"; + /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ + private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners"; + + public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; + public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args"; + private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600; + public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + Bundle args = new Bundle(); + String prefKey = getIntent().getStringExtra(EXTRA_FRAGMENT_ARG_KEY); + if (!TextUtils.isEmpty(prefKey)) { + args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey); + } + + Fragment f = Fragment.instantiate( + this, getString(R.string.settings_fragment_name), args); + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(android.R.id.content, f) + .commit(); + } + } + + private boolean startFragment(String fragment, Bundle args, String key) { + if (Utilities.ATLEAST_P && getFragmentManager().isStateSaved()) { + // Sometimes onClick can come after onPause because of being posted on the handler. + // Skip starting new fragments in that case. + return false; + } + Fragment f = Fragment.instantiate(this, fragment, args); + if (f instanceof DialogFragment) { + ((DialogFragment) f).show(getFragmentManager(), key); + } else { + getFragmentManager() + .beginTransaction() + .replace(android.R.id.content, f) + .addToBackStack(key) + .commit(); + } + return true; + } + + @Override + public boolean onPreferenceStartFragment( + PreferenceFragment preferenceFragment, Preference pref) { + return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey()); + } + + @Override + public boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref) { + Bundle args = new Bundle(); + args.putString(PreferenceFragment.ARG_PREFERENCE_ROOT, pref.getKey()); + return startFragment(getString(R.string.settings_fragment_name), args, pref.getKey()); + } + + /** + * This fragment shows the launcher preferences. + */ + public static class LauncherSettingsFragment extends PreferenceFragment { + + private SecureSettingsObserver mIconBadgingObserver; + + private String mHighLightKey; + private boolean mPreferenceHighlighted = false; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + final Bundle args = getArguments(); + mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_ARG_KEY); + if (rootKey == null && !TextUtils.isEmpty(mHighLightKey)) { + rootKey = getParentKeyForPref(mHighLightKey); + } + + if (savedInstanceState != null) { + mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY); + } + + getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY); + setPreferencesFromResource(R.xml.launcher_preferences, rootKey); + + PreferenceScreen screen = getPreferenceScreen(); + for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) { + Preference preference = screen.getPreference(i); + if (!initPreference(preference)) { + screen.removePreference(preference); + } + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted); + } + + protected String getParentKeyForPref(String key) { + return null; + } + + /** + * Initializes a preference. This is called for every preference. Returning false here + * will remove that preference from the list. + */ + protected boolean initPreference(Preference preference) { + switch (preference.getKey()) { + case ICON_BADGING_PREFERENCE_KEY: + if (!Utilities.ATLEAST_OREO || + !getResources().getBoolean(R.bool.notification_badging_enabled)) { + return false; + } + + // Listen to system notification badge settings while this UI is active. + mIconBadgingObserver = newNotificationSettingsObserver( + getActivity(), (IconBadgingPreference) preference); + mIconBadgingObserver.register(); + // Also listen if notification permission changes + mIconBadgingObserver.getResolver().registerContentObserver( + Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false, + mIconBadgingObserver); + mIconBadgingObserver.dispatchOnChange(); + return true; + + case ADD_ICON_PREFERENCE_KEY: + return Utilities.ATLEAST_OREO; + + case IconShapeOverride.KEY_PREFERENCE: + if (!IconShapeOverride.isSupported(getActivity())) { + return false; + } + IconShapeOverride.handlePreferenceUi((ListPreference) preference); + return true; + + case ALLOW_ROTATION_PREFERENCE_KEY: + if (getResources().getBoolean(R.bool.allow_rotation)) { + // Launcher supports rotation by default. No need to show this setting. + return false; + } + // Initialize the UI once + preference.setDefaultValue(getAllowRotationDefaultValue()); + return true; + + case FLAGS_PREFERENCE_KEY: + // Only show flag toggler UI if this build variant implements that. + return FeatureFlags.showFlagTogglerUi(); + } + + return true; + } + + @Override + public void onResume() { + super.onResume(); + + if (isAdded() && !mPreferenceHighlighted) { + PreferenceHighlighter highlighter = createHighlighter(); + if (highlighter != null) { + getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS); + mPreferenceHighlighted = true; + } + } + } + + private PreferenceHighlighter createHighlighter() { + if (TextUtils.isEmpty(mHighLightKey)) { + return null; + } + + PreferenceScreen screen = getPreferenceScreen(); + if (screen == null) { + return null; + } + + RecyclerView list = getListView(); + PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter(); + int position = callback.getPreferenceAdapterPosition(mHighLightKey); + return position >= 0 ? new PreferenceHighlighter(list, position) : null; + } + + @Override + public void onDestroy() { + if (mIconBadgingObserver != null) { + mIconBadgingObserver.unregister(); + mIconBadgingObserver = null; + } + super.onDestroy(); + } + } +} |