/* * 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.systemui.tuner; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.text.TextUtils; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.preference.Preference; import androidx.preference.PreferenceFragment; import androidx.preference.SwitchPreference; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.IntentButtonProvider.IntentButton; import com.android.systemui.statusbar.ScalingDrawableWrapper; import com.android.systemui.statusbar.phone.ExpandableIndicator; import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory; import com.android.systemui.tuner.ShortcutParser.Shortcut; import com.android.systemui.tuner.TunerService.Tunable; import java.util.ArrayList; import java.util.Map; import java.util.function.Consumer; public class LockscreenFragment extends PreferenceFragment { private static final String KEY_LEFT = "left"; private static final String KEY_RIGHT = "right"; private static final String KEY_CUSTOMIZE = "customize"; private static final String KEY_SHORTCUT = "shortcut"; public static final String LOCKSCREEN_LEFT_BUTTON = "sysui_keyguard_left"; public static final String LOCKSCREEN_LEFT_UNLOCK = "sysui_keyguard_left_unlock"; public static final String LOCKSCREEN_RIGHT_BUTTON = "sysui_keyguard_right"; public static final String LOCKSCREEN_RIGHT_UNLOCK = "sysui_keyguard_right_unlock"; private final ArrayList mTunables = new ArrayList<>(); private TunerService mTunerService; private Handler mHandler; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mTunerService = Dependency.get(TunerService.class); mHandler = new Handler(); addPreferencesFromResource(R.xml.lockscreen_settings); setupGroup(LOCKSCREEN_LEFT_BUTTON, LOCKSCREEN_LEFT_UNLOCK); setupGroup(LOCKSCREEN_RIGHT_BUTTON, LOCKSCREEN_RIGHT_UNLOCK); } @Override public void onDestroy() { super.onDestroy(); mTunables.forEach(t -> mTunerService.removeTunable(t)); } private void setupGroup(String buttonSetting, String unlockKey) { Preference shortcut = findPreference(buttonSetting); SwitchPreference unlock = (SwitchPreference) findPreference(unlockKey); addTunable((k, v) -> { boolean visible = !TextUtils.isEmpty(v); unlock.setVisible(visible); setSummary(shortcut, v); }, buttonSetting); } private void showSelectDialog(String buttonSetting) { RecyclerView v = (RecyclerView) LayoutInflater.from(getContext()) .inflate(R.layout.tuner_shortcut_list, null); v.setLayoutManager(new LinearLayoutManager(getContext())); AlertDialog dialog = new Builder(getContext()) .setView(v) .show(); Adapter adapter = new Adapter(getContext(), item -> { mTunerService.setValue(buttonSetting, item.getSettingValue()); dialog.dismiss(); }); v.setAdapter(adapter); } private void setSummary(Preference shortcut, String value) { if (value == null) { shortcut.setSummary(R.string.lockscreen_none); return; } if (value.contains("::")) { Shortcut info = getShortcutInfo(getContext(), value); shortcut.setSummary(info != null ? info.label : null); } else if (value.contains("/")) { ActivityInfo info = getActivityinfo(getContext(), value); shortcut.setSummary(info != null ? info.loadLabel(getContext().getPackageManager()) : null); } else { shortcut.setSummary(R.string.lockscreen_none); } } private void addTunable(Tunable t, String... keys) { mTunables.add(t); mTunerService.addTunable(t, keys); } public static ActivityInfo getActivityinfo(Context context, String value) { ComponentName component = ComponentName.unflattenFromString(value); try { return context.getPackageManager().getActivityInfo(component, 0); } catch (NameNotFoundException e) { return null; } } public static Shortcut getShortcutInfo(Context context, String value) { return Shortcut.create(context, value); } public static class Holder extends ViewHolder { public final ImageView icon; public final TextView title; public final ExpandableIndicator expand; public Holder(View itemView) { super(itemView); icon = (ImageView) itemView.findViewById(android.R.id.icon); title = (TextView) itemView.findViewById(android.R.id.title); expand = (ExpandableIndicator) itemView.findViewById(R.id.expand); } } private static class StaticShortcut extends Item { private final Context mContext; private final Shortcut mShortcut; public StaticShortcut(Context context, Shortcut shortcut) { mContext = context; mShortcut = shortcut; } @Override public Drawable getDrawable() { return mShortcut.icon.loadDrawable(mContext); } @Override public String getLabel() { return mShortcut.label; } @Override public String getSettingValue() { return mShortcut.toString(); } @Override public Boolean getExpando() { return null; } } private static class App extends Item { private final Context mContext; private final LauncherActivityInfo mInfo; private final ArrayList mChildren = new ArrayList<>(); private boolean mExpanded; public App(Context context, LauncherActivityInfo info) { mContext = context; mInfo = info; mExpanded = false; } public void addChild(Item child) { mChildren.add(child); } @Override public Drawable getDrawable() { return mInfo.getBadgedIcon(mContext.getResources().getConfiguration().densityDpi); } @Override public String getLabel() { return mInfo.getLabel().toString(); } @Override public String getSettingValue() { return mInfo.getComponentName().flattenToString(); } @Override public Boolean getExpando() { return mChildren.size() != 0 ? mExpanded : null; } @Override public void toggleExpando(Adapter adapter) { mExpanded = !mExpanded; if (mExpanded) { mChildren.forEach(child -> adapter.addItem(this, child)); } else { mChildren.forEach(child -> adapter.remItem(child)); } } } private abstract static class Item { public abstract Drawable getDrawable(); public abstract String getLabel(); public abstract String getSettingValue(); public abstract Boolean getExpando(); public void toggleExpando(Adapter adapter) { } } public static class Adapter extends RecyclerView.Adapter { private ArrayList mItems = new ArrayList<>(); private final Context mContext; private final Consumer mCallback; public Adapter(Context context, Consumer callback) { mContext = context; mCallback = callback; } @Override public Holder onCreateViewHolder(ViewGroup parent, int viewType) { return new Holder(LayoutInflater.from(parent.getContext()) .inflate(R.layout.tuner_shortcut_item, parent, false)); } @Override public void onBindViewHolder(Holder holder, int position) { Item item = mItems.get(position); holder.icon.setImageDrawable(item.getDrawable()); holder.title.setText(item.getLabel()); holder.itemView.setOnClickListener( v -> mCallback.accept(mItems.get(holder.getAdapterPosition()))); Boolean expando = item.getExpando(); if (expando != null) { holder.expand.setVisibility(View.VISIBLE); holder.expand.setExpanded(expando); holder.expand.setOnClickListener( v -> mItems.get(holder.getAdapterPosition()).toggleExpando(Adapter.this)); } else { holder.expand.setVisibility(View.GONE); } } @Override public int getItemCount() { return mItems.size(); } public void addItem(Item item) { mItems.add(item); notifyDataSetChanged(); } public void remItem(Item item) { int index = mItems.indexOf(item); mItems.remove(item); notifyItemRemoved(index); } public void addItem(Item parent, Item child) { int index = mItems.indexOf(parent); mItems.add(index + 1, child); notifyItemInserted(index + 1); } } public static class LockButtonFactory implements TunerFactory { private final String mKey; private final Context mContext; public LockButtonFactory(Context context, String key) { mContext = context; mKey = key; } @Override public String[] keys() { return new String[]{mKey}; } @Override public IntentButton create(Map settings) { String buttonStr = settings.get(mKey); if (!TextUtils.isEmpty(buttonStr)) { if (buttonStr.contains("::")) { Shortcut shortcut = getShortcutInfo(mContext, buttonStr); if (shortcut != null) { return new ShortcutButton(mContext, shortcut); } } else if (buttonStr.contains("/")) { ActivityInfo info = getActivityinfo(mContext, buttonStr); if (info != null) { return new ActivityButton(mContext, info); } } } return null; } } private static class ShortcutButton implements IntentButton { private final Shortcut mShortcut; private final IconState mIconState; public ShortcutButton(Context context, Shortcut shortcut) { mShortcut = shortcut; mIconState = new IconState(); mIconState.isVisible = true; mIconState.drawable = shortcut.icon.loadDrawable(context).mutate(); mIconState.contentDescription = mShortcut.label; int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, context.getResources().getDisplayMetrics()); mIconState.drawable = new ScalingDrawableWrapper(mIconState.drawable, size / (float) mIconState.drawable.getIntrinsicWidth()); mIconState.tint = false; } @Override public IconState getIcon() { return mIconState; } @Override public Intent getIntent() { return mShortcut.intent; } } private static class ActivityButton implements IntentButton { private final Intent mIntent; private final IconState mIconState; public ActivityButton(Context context, ActivityInfo info) { mIntent = new Intent().setComponent(new ComponentName(info.packageName, info.name)); mIconState = new IconState(); mIconState.isVisible = true; mIconState.drawable = info.loadIcon(context.getPackageManager()).mutate(); mIconState.contentDescription = info.loadLabel(context.getPackageManager()); int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, context.getResources().getDisplayMetrics()); mIconState.drawable = new ScalingDrawableWrapper(mIconState.drawable, size / (float) mIconState.drawable.getIntrinsicWidth()); mIconState.tint = false; } @Override public IconState getIcon() { return mIconState; } @Override public Intent getIntent() { return mIntent; } } }