Merge "Tuner: Allow lockscreen launch targets to be customized"
This commit is contained in:
47
packages/SystemUI/res/layout/tuner_shortcut_item.xml
Normal file
47
packages/SystemUI/res/layout/tuner_shortcut_item.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:clickable="true"
|
||||
android:gravity="center"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView android:id="@android:id/title"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.phone.ExpandableIndicator
|
||||
android:id="@+id/expand"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="12dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
22
packages/SystemUI/res/layout/tuner_shortcut_list.xml
Normal file
22
packages/SystemUI/res/layout/tuner_shortcut_list.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="200dp"
|
||||
android:gravity="center" />
|
||||
@@ -1785,5 +1785,19 @@
|
||||
<!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
|
||||
<string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
|
||||
|
||||
<!-- SysUI Tuner: Group of settings for left lock screen affordance [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_left">Left</string>
|
||||
|
||||
<!-- SysUI Tuner: Group of settings for right lock screen affordance [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_right">Right</string>
|
||||
|
||||
<!-- SysUI Tuner: Switch controlling whether to customize lock screen button [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_customize">Customize shortcut</string>
|
||||
|
||||
<!-- SysUI Tuner: Button to select lock screen shortcut [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_shortcut">Shortcut</string>
|
||||
|
||||
<!-- SysUI Tuner: Switch to control if device gets unlocked [CHAR LIMIT=60] -->
|
||||
<string name="lockscreen_unlock">Prompt for password</string>
|
||||
|
||||
</resources>
|
||||
|
||||
59
packages/SystemUI/res/xml/lockscreen_settings.xml
Normal file
59
packages/SystemUI/res/xml/lockscreen_settings.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:sysui="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/other">
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="left"
|
||||
android:title="@string/lockscreen_left">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="customize"
|
||||
android:title="@string/lockscreen_customize" />
|
||||
|
||||
<Preference
|
||||
android:key="shortcut"
|
||||
android:title="@string/lockscreen_shortcut" />
|
||||
|
||||
<com.android.systemui.tuner.TunerSwitch
|
||||
android:key="sysui_keyguard_left_unlock"
|
||||
android:title="@string/lockscreen_unlock"
|
||||
sysui:defValue="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="right"
|
||||
android:title="@string/lockscreen_right">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="customize"
|
||||
android:title="@string/lockscreen_customize" />
|
||||
|
||||
<Preference
|
||||
android:key="shortcut"
|
||||
android:title="@string/lockscreen_shortcut" />
|
||||
|
||||
<com.android.systemui.tuner.TunerSwitch
|
||||
android:key="sysui_keyguard_right_unlock"
|
||||
android:title="@string/lockscreen_unlock"
|
||||
sysui:defValue="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -155,6 +155,11 @@
|
||||
android:title="@string/nav_bar"
|
||||
android:fragment="com.android.systemui.tuner.NavBarTuner" />
|
||||
|
||||
<Preference
|
||||
android:key="lockscreen"
|
||||
android:title="@string/accessibility_desc_lock_screen"
|
||||
android:fragment="com.android.systemui.tuner.LockscreenFragment" />
|
||||
|
||||
<Preference
|
||||
android:key="other"
|
||||
android:title="@string/other"
|
||||
|
||||
@@ -18,6 +18,11 @@ package com.android.systemui.statusbar.phone;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
|
||||
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
|
||||
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
|
||||
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
|
||||
import static com.android.systemui.tuner.LockscreenFragment.getIntentButton;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
@@ -31,7 +36,9 @@ import android.content.ServiceConnection;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
@@ -43,6 +50,7 @@ import android.os.UserHandle;
|
||||
import android.provider.MediaStore;
|
||||
import android.service.media.CameraPrewarmService;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
@@ -74,6 +82,9 @@ import com.android.systemui.statusbar.KeyguardIndicationController;
|
||||
import com.android.systemui.statusbar.policy.AccessibilityController;
|
||||
import com.android.systemui.statusbar.policy.FlashlightController;
|
||||
import com.android.systemui.statusbar.policy.PreviewInflater;
|
||||
import com.android.systemui.tuner.LockscreenFragment;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.tuner.TunerService.Tunable;
|
||||
|
||||
/**
|
||||
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
|
||||
@@ -81,7 +92,8 @@ import com.android.systemui.statusbar.policy.PreviewInflater;
|
||||
*/
|
||||
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
|
||||
UnlockMethodCache.OnUnlockMethodChangedListener,
|
||||
AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
|
||||
AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener,
|
||||
Tunable {
|
||||
|
||||
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
|
||||
|
||||
@@ -148,7 +160,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
private Drawable mLeftAssistIcon;
|
||||
|
||||
private IntentButton mRightButton = new DefaultRightButton();
|
||||
private IntentButton mRightDefault = mRightButton;
|
||||
private IntentButton mRightPlugin;
|
||||
private String mRightButtonStr;
|
||||
private IntentButton mLeftButton = new DefaultLeftButton();
|
||||
private IntentButton mLeftDefault = mLeftButton;
|
||||
private IntentButton mLeftPlugin;
|
||||
private String mLeftButtonStr;
|
||||
|
||||
public KeyguardBottomAreaView(Context context) {
|
||||
this(context, null);
|
||||
@@ -245,6 +263,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
mRightListener, IntentButtonProvider.VERSION, false /* Only allow one */);
|
||||
PluginManager.getInstance(getContext()).addPluginListener(LEFT_BUTTON_PLUGIN,
|
||||
mLeftListener, IntentButtonProvider.VERSION, false /* Only allow one */);
|
||||
TunerService.get(getContext()).addTunable(this, LockscreenFragment.LOCKSCREEN_LEFT_BUTTON,
|
||||
LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,6 +272,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
super.onDetachedFromWindow();
|
||||
PluginManager.getInstance(getContext()).removePluginListener(mRightListener);
|
||||
PluginManager.getInstance(getContext()).removePluginListener(mLeftListener);
|
||||
TunerService.get(getContext()).removeTunable(this);
|
||||
}
|
||||
|
||||
private void initAccessibility() {
|
||||
@@ -552,8 +573,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
if (mPhoneStatusBar.isKeyguardCurrentlySecure()) {
|
||||
AsyncTask.execute(runnable);
|
||||
} else {
|
||||
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
|
||||
&& TunerService.get(getContext()).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
|
||||
mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
|
||||
false /* dismissShade */, false /* afterKeyguardGone */, true /* deferred */);
|
||||
dismissShade, false /* afterKeyguardGone */, true /* deferred */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,7 +594,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mActivityStarter.startActivity(mLeftButton.getIntent(), false /* dismissShade */);
|
||||
boolean dismissShade = !TextUtils.isEmpty(mLeftButtonStr)
|
||||
&& TunerService.get(getContext()).getValue(LOCKSCREEN_LEFT_UNLOCK, 1) != 0;
|
||||
mActivityStarter.startActivity(mLeftButton.getIntent(), dismissShade);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,15 +788,33 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
inflateCameraPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTuningChanged(String key, String newValue) {
|
||||
if (LockscreenFragment.LOCKSCREEN_LEFT_BUTTON.equals(key)) {
|
||||
mLeftButtonStr = newValue;
|
||||
mLeftIsVoiceAssist = TextUtils.isEmpty(mLeftButtonStr) && mLeftPlugin == null;
|
||||
mLeftButton = getIntentButton(mContext, mLeftButtonStr, mLeftPlugin, mLeftDefault);
|
||||
updateLeftAffordance();
|
||||
} else {
|
||||
mRightButtonStr = newValue;
|
||||
mRightButton = getIntentButton(mContext, mRightButtonStr, mRightPlugin, mRightDefault);
|
||||
updateRightAffordanceIcon();
|
||||
updateCameraVisibility();
|
||||
inflateCameraPreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void setRightButton(IntentButton button) {
|
||||
mRightButton = button;
|
||||
mRightPlugin = button;
|
||||
mRightButton = getIntentButton(mContext, mRightButtonStr, mRightPlugin, mRightDefault);
|
||||
updateRightAffordanceIcon();
|
||||
updateCameraVisibility();
|
||||
inflateCameraPreview();
|
||||
}
|
||||
|
||||
private void setLeftButton(IntentButton button) {
|
||||
mLeftButton = button;
|
||||
mLeftPlugin = button;
|
||||
mLeftButton = getIntentButton(mContext, mLeftButtonStr, mLeftPlugin, mLeftDefault);
|
||||
mLeftIsVoiceAssist = false;
|
||||
updateLeftAffordance();
|
||||
}
|
||||
@@ -785,7 +828,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
|
||||
@Override
|
||||
public void onPluginDisconnected(IntentButtonProvider plugin) {
|
||||
setRightButton(new DefaultRightButton());
|
||||
setRightButton(null);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -798,7 +841,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
|
||||
@Override
|
||||
public void onPluginDisconnected(IntentButtonProvider plugin) {
|
||||
setLeftButton(new DefaultLeftButton());
|
||||
setLeftButton(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* 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.LauncherApps;
|
||||
import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Process;
|
||||
import android.support.v14.preference.PreferenceFragment;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceGroup;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
|
||||
import com.android.systemui.statusbar.phone.ExpandableIndicator;
|
||||
import com.android.systemui.tuner.ShortcutParser.Shortcut;
|
||||
import com.android.systemui.tuner.TunerService.Tunable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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<Tunable> mTunables = new ArrayList<>();
|
||||
private TunerService mTunerService;
|
||||
private Handler mHandler;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
mTunerService = TunerService.get(getContext());
|
||||
mHandler = new Handler();
|
||||
addPreferencesFromResource(R.xml.lockscreen_settings);
|
||||
setupGroup((PreferenceGroup) findPreference(KEY_LEFT), LOCKSCREEN_LEFT_BUTTON,
|
||||
LOCKSCREEN_LEFT_UNLOCK);
|
||||
setupGroup((PreferenceGroup) findPreference(KEY_RIGHT), LOCKSCREEN_RIGHT_BUTTON,
|
||||
LOCKSCREEN_RIGHT_UNLOCK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mTunables.forEach(t -> mTunerService.removeTunable(t));
|
||||
}
|
||||
|
||||
private void setupGroup(PreferenceGroup group, String buttonSetting, String unlockKey) {
|
||||
SwitchPreference customize = (SwitchPreference) group.findPreference(KEY_CUSTOMIZE);
|
||||
Preference shortcut = group.findPreference(KEY_SHORTCUT);
|
||||
SwitchPreference unlock = (SwitchPreference) group.findPreference(unlockKey);
|
||||
addTunable((k, v) -> {
|
||||
boolean visible = v != null;
|
||||
customize.setChecked(visible);
|
||||
shortcut.setVisible(visible);
|
||||
unlock.setVisible(visible);
|
||||
if (visible) {
|
||||
setSummary(shortcut, v);
|
||||
}
|
||||
}, buttonSetting);
|
||||
customize.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
boolean hasSetting = mTunerService.getValue(buttonSetting) != null;
|
||||
if (hasSetting != (boolean) newValue) {
|
||||
mHandler.post(() -> mTunerService.setValue(buttonSetting, hasSetting ? null : ""));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
shortcut.setOnPreferenceClickListener(preference -> {
|
||||
showSelectDialog(buttonSetting);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
LauncherApps apps = getContext().getSystemService(LauncherApps.class);
|
||||
List<LauncherActivityInfo> activities = apps.getActivityList(null,
|
||||
Process.myUserHandle());
|
||||
|
||||
activities.forEach(info -> {
|
||||
App app = new App(getContext(), info);
|
||||
try {
|
||||
new ShortcutParser(getContext(), info.getComponentName()).getShortcuts().forEach(
|
||||
shortcut -> app.addChild(new StaticShortcut(getContext(), shortcut)));
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
adapter.addItem(app);
|
||||
});
|
||||
|
||||
v.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setSummary(Preference shortcut, String value) {
|
||||
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(null);
|
||||
}
|
||||
}
|
||||
|
||||
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<Item> 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<Holder> {
|
||||
private ArrayList<Item> mItems = new ArrayList<>();
|
||||
private final Context mContext;
|
||||
private final Consumer<Item> mCallback;
|
||||
|
||||
public Adapter(Context context, Consumer<Item> 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 IntentButton getIntentButton(Context context, String buttonStr,
|
||||
IntentButton plugin, IntentButton def) {
|
||||
// Plugin wins.
|
||||
if (plugin != null) return plugin;
|
||||
// Then tuner options.
|
||||
if (!TextUtils.isEmpty(buttonStr)) {
|
||||
if (buttonStr.contains("::")) {
|
||||
Shortcut shortcut = getShortcutInfo(context, buttonStr);
|
||||
if (shortcut != null) {
|
||||
return new ShortcutButton(context, shortcut);
|
||||
}
|
||||
} else if (buttonStr.contains("/")) {
|
||||
ActivityInfo info = getActivityinfo(context, buttonStr);
|
||||
if (info != null) {
|
||||
return new ActivityButton(context, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then default.
|
||||
return def;
|
||||
}
|
||||
|
||||
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);
|
||||
mIconState.contentDescription = mShortcut.label;
|
||||
}
|
||||
|
||||
@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());
|
||||
mIconState.contentDescription = info.loadLabel(context.getPackageManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IconState getIcon() {
|
||||
return mIconState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return mIntent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ShortcutParser {
|
||||
private static final String SHORTCUTS = "android.app.shortcuts";
|
||||
private static final String SHORTCUT = "shortcut";
|
||||
private static final String INTENT = "intent";
|
||||
|
||||
private final Context mContext;
|
||||
private final String mPkg;
|
||||
private final int mResId;
|
||||
private final String mName;
|
||||
private Resources mResources;
|
||||
private AttributeSet mAttrs;
|
||||
|
||||
public ShortcutParser(Context context, ComponentName component) throws NameNotFoundException {
|
||||
this(context, component.getPackageName(), component.getClassName(),
|
||||
getResId(context, component));
|
||||
}
|
||||
|
||||
private static int getResId(Context context, ComponentName component)
|
||||
throws NameNotFoundException {
|
||||
ActivityInfo i = context.getPackageManager().getActivityInfo(
|
||||
component, PackageManager.GET_META_DATA);
|
||||
int resId = 0;
|
||||
if (i.metaData != null && i.metaData.containsKey(SHORTCUTS)) {
|
||||
resId = i.metaData.getInt(SHORTCUTS);
|
||||
}
|
||||
return resId;
|
||||
}
|
||||
|
||||
public ShortcutParser(Context context, String pkg, String name, int resId) {
|
||||
mContext = context;
|
||||
mPkg = pkg;
|
||||
mResId = resId;
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public List<Shortcut> getShortcuts() {
|
||||
List<Shortcut> list = new ArrayList<>();
|
||||
if (mResId != 0) {
|
||||
try {
|
||||
mResources = mContext.getPackageManager().getResourcesForApplication(mPkg);
|
||||
XmlResourceParser parser = mResources.getXml(mResId);
|
||||
mAttrs = Xml.asAttributeSet(parser);
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
if (parser.getName().equals(SHORTCUT)) {
|
||||
Shortcut c = parseShortcut(parser);
|
||||
if (c != null) {
|
||||
list.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private Shortcut parseShortcut(XmlResourceParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
final TypedArray sa = mResources.obtainAttributes(mAttrs, R.styleable.Shortcut);
|
||||
Shortcut c = new Shortcut();
|
||||
|
||||
final boolean enabled = sa.getBoolean(R.styleable.Shortcut_enabled, true);
|
||||
if (!enabled) return null;
|
||||
final String id = sa.getString(R.styleable.Shortcut_shortcutId);
|
||||
final int iconResId = sa.getResourceId(R.styleable.Shortcut_icon, 0);
|
||||
final int titleResId = sa.getResourceId(R.styleable.Shortcut_shortcutShortLabel, 0);
|
||||
|
||||
c.pkg = mPkg;
|
||||
c.icon = Icon.createWithResource(mPkg, iconResId);
|
||||
c.id = id;
|
||||
c.label = mResources.getString(titleResId);
|
||||
c.name = mName;
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_TAG) {
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
if (parser.getName().equals(INTENT)) {
|
||||
c.intent = Intent.parseIntent(mResources, parser, mAttrs);
|
||||
}
|
||||
}
|
||||
return c.intent != null ? c : null;
|
||||
}
|
||||
|
||||
public static class Shortcut {
|
||||
public Intent intent;
|
||||
public String label;
|
||||
public Icon icon;
|
||||
public String pkg;
|
||||
public String id;
|
||||
public String name;
|
||||
|
||||
public static Shortcut create(Context context, String value) {
|
||||
String[] sp = value.split("::");
|
||||
try {
|
||||
for (Shortcut shortcut : new ShortcutParser(context,
|
||||
new ComponentName(sp[0], sp[1])).getShortcuts()) {
|
||||
if (shortcut.id.equals(sp[2])) {
|
||||
return shortcut;
|
||||
}
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(pkg);
|
||||
builder.append("::");
|
||||
builder.append(name);
|
||||
builder.append("::");
|
||||
builder.append(id);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user