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