Home | History | Annotate | Download | only in tuner
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
      5  * except in compliance with the License. You may obtain a copy of the License at
      6  *
      7  *      http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the
     10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     11  * KIND, either express or implied. See the License for the specific language governing
     12  * permissions and limitations under the License.
     13  */
     14 
     15 package com.android.systemui.tuner;
     16 
     17 import android.app.AlertDialog;
     18 import android.app.AlertDialog.Builder;
     19 import android.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.pm.ActivityInfo;
     23 import android.content.pm.LauncherActivityInfo;
     24 import android.content.pm.PackageManager.NameNotFoundException;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.text.TextUtils;
     29 import android.util.TypedValue;
     30 import android.view.LayoutInflater;
     31 import android.view.View;
     32 import android.view.ViewGroup;
     33 import android.widget.ImageView;
     34 import android.widget.TextView;
     35 
     36 import androidx.preference.Preference;
     37 import androidx.preference.PreferenceFragment;
     38 import androidx.preference.SwitchPreference;
     39 import androidx.recyclerview.widget.LinearLayoutManager;
     40 import androidx.recyclerview.widget.RecyclerView;
     41 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
     42 
     43 import com.android.systemui.Dependency;
     44 import com.android.systemui.R;
     45 import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
     46 import com.android.systemui.statusbar.ScalingDrawableWrapper;
     47 import com.android.systemui.statusbar.phone.ExpandableIndicator;
     48 import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
     49 import com.android.systemui.tuner.ShortcutParser.Shortcut;
     50 import com.android.systemui.tuner.TunerService.Tunable;
     51 
     52 import java.util.ArrayList;
     53 import java.util.Map;
     54 import java.util.function.Consumer;
     55 
     56 public class LockscreenFragment extends PreferenceFragment {
     57 
     58     private static final String KEY_LEFT = "left";
     59     private static final String KEY_RIGHT = "right";
     60     private static final String KEY_CUSTOMIZE = "customize";
     61     private static final String KEY_SHORTCUT = "shortcut";
     62 
     63     public static final String LOCKSCREEN_LEFT_BUTTON = "sysui_keyguard_left";
     64     public static final String LOCKSCREEN_LEFT_UNLOCK = "sysui_keyguard_left_unlock";
     65     public static final String LOCKSCREEN_RIGHT_BUTTON = "sysui_keyguard_right";
     66     public static final String LOCKSCREEN_RIGHT_UNLOCK = "sysui_keyguard_right_unlock";
     67 
     68     private final ArrayList<Tunable> mTunables = new ArrayList<>();
     69     private TunerService mTunerService;
     70     private Handler mHandler;
     71 
     72     @Override
     73     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
     74         mTunerService = Dependency.get(TunerService.class);
     75         mHandler = new Handler();
     76         addPreferencesFromResource(R.xml.lockscreen_settings);
     77         setupGroup(LOCKSCREEN_LEFT_BUTTON, LOCKSCREEN_LEFT_UNLOCK);
     78         setupGroup(LOCKSCREEN_RIGHT_BUTTON, LOCKSCREEN_RIGHT_UNLOCK);
     79     }
     80 
     81     @Override
     82     public void onDestroy() {
     83         super.onDestroy();
     84         mTunables.forEach(t -> mTunerService.removeTunable(t));
     85     }
     86 
     87     private void setupGroup(String buttonSetting, String unlockKey) {
     88         Preference shortcut = findPreference(buttonSetting);
     89         SwitchPreference unlock = (SwitchPreference) findPreference(unlockKey);
     90         addTunable((k, v) -> {
     91             boolean visible = !TextUtils.isEmpty(v);
     92             unlock.setVisible(visible);
     93             setSummary(shortcut, v);
     94         }, buttonSetting);
     95     }
     96 
     97     private void showSelectDialog(String buttonSetting) {
     98         RecyclerView v = (RecyclerView) LayoutInflater.from(getContext())
     99                 .inflate(R.layout.tuner_shortcut_list, null);
    100         v.setLayoutManager(new LinearLayoutManager(getContext()));
    101         AlertDialog dialog = new Builder(getContext())
    102                 .setView(v)
    103                 .show();
    104         Adapter adapter = new Adapter(getContext(), item -> {
    105             mTunerService.setValue(buttonSetting, item.getSettingValue());
    106             dialog.dismiss();
    107         });
    108 
    109         v.setAdapter(adapter);
    110     }
    111 
    112     private void setSummary(Preference shortcut, String value) {
    113         if (value == null) {
    114             shortcut.setSummary(R.string.lockscreen_none);
    115             return;
    116         }
    117         if (value.contains("::")) {
    118             Shortcut info = getShortcutInfo(getContext(), value);
    119             shortcut.setSummary(info != null ? info.label : null);
    120         } else if (value.contains("/")) {
    121             ActivityInfo info = getActivityinfo(getContext(), value);
    122             shortcut.setSummary(info != null ? info.loadLabel(getContext().getPackageManager())
    123                     : null);
    124         } else {
    125             shortcut.setSummary(R.string.lockscreen_none);
    126         }
    127     }
    128 
    129     private void addTunable(Tunable t, String... keys) {
    130         mTunables.add(t);
    131         mTunerService.addTunable(t, keys);
    132     }
    133 
    134     public static ActivityInfo getActivityinfo(Context context, String value) {
    135         ComponentName component = ComponentName.unflattenFromString(value);
    136         try {
    137             return context.getPackageManager().getActivityInfo(component, 0);
    138         } catch (NameNotFoundException e) {
    139             return null;
    140         }
    141     }
    142 
    143     public static Shortcut getShortcutInfo(Context context, String value) {
    144         return Shortcut.create(context, value);
    145     }
    146 
    147     public static class Holder extends ViewHolder {
    148         public final ImageView icon;
    149         public final TextView title;
    150         public final ExpandableIndicator expand;
    151 
    152         public Holder(View itemView) {
    153             super(itemView);
    154             icon = (ImageView) itemView.findViewById(android.R.id.icon);
    155             title = (TextView) itemView.findViewById(android.R.id.title);
    156             expand = (ExpandableIndicator) itemView.findViewById(R.id.expand);
    157         }
    158     }
    159 
    160     private static class StaticShortcut extends Item {
    161 
    162         private final Context mContext;
    163         private final Shortcut mShortcut;
    164 
    165 
    166         public StaticShortcut(Context context, Shortcut shortcut) {
    167             mContext = context;
    168             mShortcut = shortcut;
    169         }
    170 
    171         @Override
    172         public Drawable getDrawable() {
    173             return mShortcut.icon.loadDrawable(mContext);
    174         }
    175 
    176         @Override
    177         public String getLabel() {
    178             return mShortcut.label;
    179         }
    180 
    181         @Override
    182         public String getSettingValue() {
    183             return mShortcut.toString();
    184         }
    185 
    186         @Override
    187         public Boolean getExpando() {
    188             return null;
    189         }
    190     }
    191 
    192     private static class App extends Item {
    193 
    194         private final Context mContext;
    195         private final LauncherActivityInfo mInfo;
    196         private final ArrayList<Item> mChildren = new ArrayList<>();
    197         private boolean mExpanded;
    198 
    199         public App(Context context, LauncherActivityInfo info) {
    200             mContext = context;
    201             mInfo = info;
    202             mExpanded = false;
    203         }
    204 
    205         public void addChild(Item child) {
    206             mChildren.add(child);
    207         }
    208 
    209         @Override
    210         public Drawable getDrawable() {
    211             return mInfo.getBadgedIcon(mContext.getResources().getConfiguration().densityDpi);
    212         }
    213 
    214         @Override
    215         public String getLabel() {
    216             return mInfo.getLabel().toString();
    217         }
    218 
    219         @Override
    220         public String getSettingValue() {
    221             return mInfo.getComponentName().flattenToString();
    222         }
    223 
    224         @Override
    225         public Boolean getExpando() {
    226             return mChildren.size() != 0 ? mExpanded : null;
    227         }
    228 
    229         @Override
    230         public void toggleExpando(Adapter adapter) {
    231             mExpanded = !mExpanded;
    232             if (mExpanded) {
    233                 mChildren.forEach(child -> adapter.addItem(this, child));
    234             } else {
    235                 mChildren.forEach(child -> adapter.remItem(child));
    236             }
    237         }
    238     }
    239 
    240     private abstract static class Item {
    241         public abstract Drawable getDrawable();
    242 
    243         public abstract String getLabel();
    244 
    245         public abstract String getSettingValue();
    246 
    247         public abstract Boolean getExpando();
    248 
    249         public void toggleExpando(Adapter adapter) {
    250         }
    251     }
    252 
    253     public static class Adapter extends RecyclerView.Adapter<Holder> {
    254         private ArrayList<Item> mItems = new ArrayList<>();
    255         private final Context mContext;
    256         private final Consumer<Item> mCallback;
    257 
    258         public Adapter(Context context, Consumer<Item> callback) {
    259             mContext = context;
    260             mCallback = callback;
    261         }
    262 
    263         @Override
    264         public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
    265             return new Holder(LayoutInflater.from(parent.getContext())
    266                     .inflate(R.layout.tuner_shortcut_item, parent, false));
    267         }
    268 
    269         @Override
    270         public void onBindViewHolder(Holder holder, int position) {
    271             Item item = mItems.get(position);
    272             holder.icon.setImageDrawable(item.getDrawable());
    273             holder.title.setText(item.getLabel());
    274             holder.itemView.setOnClickListener(
    275                     v -> mCallback.accept(mItems.get(holder.getAdapterPosition())));
    276             Boolean expando = item.getExpando();
    277             if (expando != null) {
    278                 holder.expand.setVisibility(View.VISIBLE);
    279                 holder.expand.setExpanded(expando);
    280                 holder.expand.setOnClickListener(
    281                         v -> mItems.get(holder.getAdapterPosition()).toggleExpando(Adapter.this));
    282             } else {
    283                 holder.expand.setVisibility(View.GONE);
    284             }
    285         }
    286 
    287         @Override
    288         public int getItemCount() {
    289             return mItems.size();
    290         }
    291 
    292         public void addItem(Item item) {
    293             mItems.add(item);
    294             notifyDataSetChanged();
    295         }
    296 
    297         public void remItem(Item item) {
    298             int index = mItems.indexOf(item);
    299             mItems.remove(item);
    300             notifyItemRemoved(index);
    301         }
    302 
    303         public void addItem(Item parent, Item child) {
    304             int index = mItems.indexOf(parent);
    305             mItems.add(index + 1, child);
    306             notifyItemInserted(index + 1);
    307         }
    308     }
    309 
    310     public static class LockButtonFactory implements TunerFactory<IntentButton> {
    311 
    312         private final String mKey;
    313         private final Context mContext;
    314 
    315         public LockButtonFactory(Context context, String key) {
    316             mContext = context;
    317             mKey = key;
    318         }
    319 
    320         @Override
    321         public String[] keys() {
    322             return new String[]{mKey};
    323         }
    324 
    325         @Override
    326         public IntentButton create(Map<String, String> settings) {
    327             String buttonStr = settings.get(mKey);
    328             if (!TextUtils.isEmpty(buttonStr)) {
    329                 if (buttonStr.contains("::")) {
    330                     Shortcut shortcut = getShortcutInfo(mContext, buttonStr);
    331                     if (shortcut != null) {
    332                         return new ShortcutButton(mContext, shortcut);
    333                     }
    334                 } else if (buttonStr.contains("/")) {
    335                     ActivityInfo info = getActivityinfo(mContext, buttonStr);
    336                     if (info != null) {
    337                         return new ActivityButton(mContext, info);
    338                     }
    339                 }
    340             }
    341             return null;
    342         }
    343     }
    344 
    345     private static class ShortcutButton implements IntentButton {
    346         private final Shortcut mShortcut;
    347         private final IconState mIconState;
    348 
    349         public ShortcutButton(Context context, Shortcut shortcut) {
    350             mShortcut = shortcut;
    351             mIconState = new IconState();
    352             mIconState.isVisible = true;
    353             mIconState.drawable = shortcut.icon.loadDrawable(context).mutate();
    354             mIconState.contentDescription = mShortcut.label;
    355             int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32,
    356                     context.getResources().getDisplayMetrics());
    357             mIconState.drawable = new ScalingDrawableWrapper(mIconState.drawable,
    358                     size / (float) mIconState.drawable.getIntrinsicWidth());
    359             mIconState.tint = false;
    360         }
    361 
    362         @Override
    363         public IconState getIcon() {
    364             return mIconState;
    365         }
    366 
    367         @Override
    368         public Intent getIntent() {
    369             return mShortcut.intent;
    370         }
    371     }
    372 
    373     private static class ActivityButton implements IntentButton {
    374         private final Intent mIntent;
    375         private final IconState mIconState;
    376 
    377         public ActivityButton(Context context, ActivityInfo info) {
    378             mIntent = new Intent().setComponent(new ComponentName(info.packageName, info.name));
    379             mIconState = new IconState();
    380             mIconState.isVisible = true;
    381             mIconState.drawable = info.loadIcon(context.getPackageManager()).mutate();
    382             mIconState.contentDescription = info.loadLabel(context.getPackageManager());
    383             int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32,
    384                     context.getResources().getDisplayMetrics());
    385             mIconState.drawable = new ScalingDrawableWrapper(mIconState.drawable,
    386                     size / (float) mIconState.drawable.getIntrinsicWidth());
    387             mIconState.tint = false;
    388         }
    389 
    390         @Override
    391         public IconState getIcon() {
    392             return mIconState;
    393         }
    394 
    395         @Override
    396         public Intent getIntent() {
    397             return mIntent;
    398         }
    399     }
    400 }
    401