am 0c3c1c99: am 7853e137: Merge "Add QS Tuner" into mnc-dev

* commit '0c3c1c995787c5d3bffbc0d01e689872254b0788':
  Add QS Tuner
This commit is contained in:
Jason Monk
2015-05-14 01:46:22 +00:00
committed by Android Git Automerger
9 changed files with 591 additions and 15 deletions

View File

@@ -184,7 +184,7 @@
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/icon"
android:icon="@*android:drawable/stat_sys_adb"
android:theme="@android:style/Theme.Material.Settings"
android:label="@string/system_ui_tuner"
android:exported="true">

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<com.android.systemui.tuner.AutoScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/system_secondary_color" >
<LinearLayout
android:id="@+id/all_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:gravity="center"
android:orientation="vertical">
<View
android:background="@color/qs_tile_divider"
android:layout_width="match_parent"
android:layout_height="2dp" />
<FrameLayout
android:id="@+id/remove_target"
android:layout_width="105dp"
android:layout_height="@dimen/qs_tile_height" />
<FrameLayout
android:id="@+id/add_target"
android:layout_width="105dp"
android:layout_height="@dimen/qs_tile_height" />
</LinearLayout>
</com.android.systemui.tuner.AutoScrollView>

View File

@@ -1024,4 +1024,13 @@
<!-- Name of special SystemUI debug settings -->
<string name="system_ui_tuner">SystemUI Tuner</string>
<!-- Name of quick settings -->
<string name="quick_settings">Quick Settings</string>
<!-- Description for adding a quick settings tile -->
<string name="add_tile">Add tile</string>
<!-- Name of a quick settings tile controlled by broadcast -->
<string name="broadcast_tile">Broadcast Tile</string>
</resources>

View File

@@ -19,4 +19,8 @@
<!-- Tuner prefs go here -->
<Preference
android:key="qs_tuner"
android:title="@string/quick_settings" />
</PreferenceScreen>

View File

@@ -50,12 +50,12 @@ public class QSPanel extends ViewGroup {
private static final float TILE_ASPECT = 1.2f;
private final Context mContext;
private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
private final View mDetail;
private final ViewGroup mDetailContent;
private final TextView mDetailSettingsButton;
private final TextView mDetailDoneButton;
private final View mBrightnessView;
protected final View mBrightnessView;
private final QSDetailClipper mClipper;
private final H mHandler = new H();
@@ -560,13 +560,13 @@ public class QSPanel extends ViewGroup {
DetailAdapter detailAdapter;
}
private static final class TileRecord extends Record {
QSTile<?> tile;
QSTileView tileView;
int row;
int col;
boolean scanState;
boolean openingDetail;
protected static final class TileRecord extends Record {
public QSTile<?> tile;
public QSTileView tileView;
public int row;
public int col;
public boolean scanState;
public boolean openingDetail;
}
private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {

View File

@@ -67,11 +67,12 @@ public class QSTileHost implements QSTile.Host {
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String TILES_SETTING = "sysui_qs_tiles";
protected static final String TILES_SETTING = "sysui_qs_tiles";
private final Context mContext;
private final PhoneStatusBar mStatusBar;
private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>();
private final ArrayList<String> mTileSpecs = new ArrayList<>();
private final Observer mObserver = new Observer();
private final BluetoothController mBluetooth;
private final LocationController mLocation;
@@ -81,7 +82,7 @@ public class QSTileHost implements QSTile.Host {
private final HotspotController mHotspot;
private final CastController mCast;
private final Looper mLooper;
private final CurrentUserTracker mUserTracker;
protected final CurrentUserTracker mUserTracker;
private final FlashlightController mFlashlight;
private final UserSwitcherController mUserSwitcherController;
private final KeyguardMonitor mKeyguard;
@@ -224,6 +225,7 @@ public class QSTileHost implements QSTile.Host {
private void recreateTiles() {
if (DEBUG) Log.d(TAG, "Recreating tiles");
final List<String> tileSpecs = loadTileSpecs();
if (tileSpecs.equals(mTileSpecs)) return;
for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
if (!tileSpecs.contains(tile.getKey())) {
if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
@@ -243,7 +245,8 @@ public class QSTileHost implements QSTile.Host {
}
}
}
if (mTiles.equals(newTiles)) return;
mTileSpecs.clear();
mTileSpecs.addAll(tileSpecs);
mTiles.clear();
mTiles.putAll(newTiles);
if (mCallback != null) {
@@ -251,7 +254,7 @@ public class QSTileHost implements QSTile.Host {
}
}
private QSTile<?> createTile(String tileSpec) {
protected QSTile<?> createTile(String tileSpec) {
if (tileSpec.equals("wifi")) return new WifiTile(this);
else if (tileSpec.equals("bt")) return new BluetoothTile(this);
else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
@@ -267,7 +270,7 @@ public class QSTileHost implements QSTile.Host {
else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
}
private List<String> loadTileSpecs() {
protected List<String> loadTileSpecs() {
final Resources res = mContext.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
String tileList = Secure.getStringForUser(mContext.getContentResolver(), TILES_SETTING,

View File

@@ -0,0 +1,46 @@
/*
* 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.systemui.tuner;
import android.content.Context;
import android.util.AttributeSet;
import android.view.DragEvent;
import android.widget.ScrollView;
public class AutoScrollView extends ScrollView {
private static final float SCROLL_PERCENT = .10f;
public AutoScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onDragEvent(DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_LOCATION:
int y = (int) event.getY();
int height = getHeight();
int scrollPadding = (int) (height * SCROLL_PERCENT);
if (y < scrollPadding) {
scrollBy(0, y - scrollPadding);
} else if (y > height - scrollPadding) {
scrollBy(0, y - height + scrollPadding);
}
break;
}
return false;
}
}

View File

@@ -0,0 +1,451 @@
/*
* 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.systemui.tuner;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.util.Log;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.QSTile.ResourceIcon;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.statusbar.phone.QSTileHost;
import java.util.List;
public class QsTuner extends Fragment implements Callback {
private static final String TAG = "QsTuner";
private static final int MENU_RESET = Menu.FIRST;
private DraggableQsPanel mQsPanel;
private CustomHost mTileHost;
private FrameLayout mDropTarget;
private ScrollView mScrollRoot;
private FrameLayout mAddTarget;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, MENU_RESET, 0, com.android.internal.R.string.reset);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_RESET:
mTileHost.reset();
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mScrollRoot = (ScrollView) inflater.inflate(R.layout.tuner_qs, container, false);
mQsPanel = new DraggableQsPanel(getContext());
mTileHost = new CustomHost(getContext());
mTileHost.setCallback(this);
mQsPanel.setTiles(mTileHost.getTiles());
mQsPanel.setHost(mTileHost);
mQsPanel.refreshAllTiles();
((ViewGroup) mScrollRoot.findViewById(R.id.all_details)).addView(mQsPanel, 0);
mDropTarget = (FrameLayout) mScrollRoot.findViewById(R.id.remove_target);
setupDropTarget();
mAddTarget = (FrameLayout) mScrollRoot.findViewById(R.id.add_target);
setupAddTarget();
return mScrollRoot;
}
private void setupDropTarget() {
QSTileView tileView = new QSTileView(getContext());
QSTile.State state = new QSTile.State();
state.visible = true;
state.icon = ResourceIcon.get(R.drawable.ic_delete);
state.label = getString(com.android.internal.R.string.delete);
tileView.onStateChanged(state);
mDropTarget.addView(tileView);
mDropTarget.setVisibility(View.GONE);
new DragHelper(tileView, new DropListener() {
@Override
public void onDrop(String sourceText) {
mTileHost.remove(sourceText);
}
});
}
private void setupAddTarget() {
QSTileView tileView = new QSTileView(getContext());
QSTile.State state = new QSTile.State();
state.visible = true;
state.icon = ResourceIcon.get(R.drawable.ic_add_circle_qs);
state.label = getString(R.string.add_tile);
tileView.onStateChanged(state);
mAddTarget.addView(tileView);
tileView.setClickable(true);
tileView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mTileHost.showAddDialog();
}
});
}
public void onStartDrag() {
mDropTarget.setVisibility(View.VISIBLE);
mAddTarget.setVisibility(View.GONE);
}
public void stopDrag() {
mDropTarget.setVisibility(View.GONE);
mAddTarget.setVisibility(View.VISIBLE);
}
@Override
public void onTilesChanged() {
mQsPanel.setTiles(mTileHost.getTiles());
}
private static int getLabelResource(String spec) {
if (spec.equals("wifi")) return R.string.quick_settings_wifi_label;
else if (spec.equals("bt")) return R.string.quick_settings_bluetooth_label;
else if (spec.equals("inversion")) return R.string.quick_settings_inversion_label;
else if (spec.equals("cell")) return R.string.quick_settings_cellular_detail_title;
else if (spec.equals("airplane")) return R.string.quick_settings_airplane_mode_label;
else if (spec.equals("dnd")) return R.string.quick_settings_dnd_label;
else if (spec.equals("rotation")) return R.string.quick_settings_rotation_locked_label;
else if (spec.equals("flashlight")) return R.string.quick_settings_flashlight_label;
else if (spec.equals("location")) return R.string.quick_settings_location_label;
else if (spec.equals("cast")) return R.string.quick_settings_cast_title;
else if (spec.equals("hotspot")) return R.string.quick_settings_hotspot_label;
return 0;
}
private static class CustomHost extends QSTileHost {
public CustomHost(Context context) {
super(context, null, null, null, null, null, null, null, null, null,
null, null, null);
}
@Override
protected QSTile<?> createTile(String tileSpec) {
return new DraggableTile(this, tileSpec);
}
public void replace(String oldTile, String newTile) {
if (oldTile.equals(newTile)) {
return;
}
List<String> order = loadTileSpecs();
int index = order.indexOf(oldTile);
if (index < 0) {
Log.e(TAG, "Can't find " + oldTile);
return;
}
order.remove(newTile);
order.add(index, newTile);
setTiles(order);
}
public void remove(String tile) {
List<String> tiles = loadTileSpecs();
tiles.remove(tile);
setTiles(tiles);
}
public void add(String tile) {
List<String> tiles = loadTileSpecs();
tiles.add(tile);
setTiles(tiles);
}
public void reset() {
Secure.putStringForUser(getContext().getContentResolver(),
TILES_SETTING, "default", mUserTracker.getCurrentUserId());
}
private void setTiles(List<String> tiles) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < tiles.size(); i++) {
if (builder.length() != 0) {
builder.append(',');
}
builder.append(tiles.get(i));
}
Secure.putStringForUser(getContext().getContentResolver(),
TILES_SETTING, builder.toString(), mUserTracker.getCurrentUserId());
}
public void showAddDialog() {
List<String> tiles = loadTileSpecs();
String[] defaults =
getContext().getString(R.string.quick_settings_tiles_default).split(",");
final String[] available = new String[defaults.length + 1 - tiles.size()];
int index = 0;
for (int i = 0; i < defaults.length; i++) {
if (tiles.contains(defaults[i])) {
continue;
}
int resource = getLabelResource(defaults[i]);
if (resource != 0) {
available[index++] = getContext().getString(resource);
} else {
available[index++] = defaults[i];
}
}
available[index++] = getContext().getString(R.string.broadcast_tile);
new AlertDialog.Builder(getContext())
.setTitle(R.string.add_tile)
.setItems(available, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which < available.length - 1) {
add(available[which]);
} else {
showBroadcastTileDialog();
}
}
}).show();
}
public void showBroadcastTileDialog() {
final EditText editText = new EditText(getContext());
new AlertDialog.Builder(getContext())
.setTitle(R.string.broadcast_tile)
.setView(editText)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String action = editText.getText().toString();
if (isValid(action)) {
add(IntentTile.PREFIX + action + ')');
}
}
}).show();
}
private boolean isValid(String action) {
for (int i = 0; i < action.length(); i++) {
char c = action.charAt(i);
if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '.') {
return false;
}
}
return true;
}
}
private static class DraggableTile extends QSTile<QSTile.State>
implements DropListener {
private String mSpec;
private QSTileView mView;
protected DraggableTile(QSTile.Host host, String tileSpec) {
super(host);
Log.d(TAG, "Creating tile " + tileSpec);
mSpec = tileSpec;
}
@Override
public QSTileView createTileView(Context context) {
mView = super.createTileView(context);
return mView;
}
@Override
public boolean supportsDualTargets() {
return "wifi".equals(mSpec) || "bt".equals(mSpec);
}
@Override
public void setListening(boolean listening) {
}
@Override
protected QSTile.State newTileState() {
return new QSTile.State();
}
@Override
protected void handleClick() {
}
@Override
protected void handleUpdateState(QSTile.State state, Object arg) {
state.visible = true;
state.icon = ResourceIcon.get(getIcon());
state.label = getLabel();
}
private String getLabel() {
int resource = getLabelResource(mSpec);
if (resource != 0) {
return mContext.getString(resource);
}
if (mSpec.startsWith(IntentTile.PREFIX)) {
int lastDot = mSpec.lastIndexOf('.');
if (lastDot >= 0) {
return mSpec.substring(lastDot + 1, mSpec.length() - 1);
} else {
return mSpec.substring(IntentTile.PREFIX.length(), mSpec.length() - 1);
}
}
return mSpec;
}
private int getIcon() {
if (mSpec.equals("wifi")) return R.drawable.ic_qs_wifi_full_3;
else if (mSpec.equals("bt")) return R.drawable.ic_qs_bluetooth_connected;
else if (mSpec.equals("inversion")) return R.drawable.ic_invert_colors_enable;
else if (mSpec.equals("cell")) return R.drawable.ic_qs_signal_full_3;
else if (mSpec.equals("airplane")) return R.drawable.ic_signal_airplane_enable;
else if (mSpec.equals("dnd")) return R.drawable.ic_qs_dnd_on;
else if (mSpec.equals("rotation")) return R.drawable.ic_portrait_from_auto_rotate;
else if (mSpec.equals("flashlight")) return R.drawable.ic_signal_flashlight_enable;
else if (mSpec.equals("location")) return R.drawable.ic_signal_location_enable;
else if (mSpec.equals("cast")) return R.drawable.ic_qs_cast_on;
else if (mSpec.equals("hotspot")) return R.drawable.ic_hotspot_enable;
return R.drawable.android;
}
@Override
public int getMetricsCategory() {
return 20000;
}
@Override
public boolean equals(Object o) {
if (o instanceof DraggableTile) {
return mSpec.equals(((DraggableTile) o).mSpec);
}
return false;
}
@Override
public void onDrop(String sourceText) {
((CustomHost) mHost).replace(mSpec, sourceText);
}
}
private class DragHelper implements OnDragListener {
private final View mView;
private final DropListener mListener;
public DragHelper(View view, DropListener dropListener) {
mView = view;
mListener = dropListener;
mView.setOnDragListener(this);
}
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_ENTERED:
mView.setBackgroundColor(0x77ffffff);
break;
case DragEvent.ACTION_DRAG_ENDED:
stopDrag();
case DragEvent.ACTION_DRAG_EXITED:
mView.setBackgroundColor(0x0);
break;
case DragEvent.ACTION_DROP:
stopDrag();
String text = event.getClipData().getItemAt(0).getText().toString();
mListener.onDrop(text);
break;
}
return true;
}
}
public interface DropListener {
void onDrop(String sourceText);
}
private class DraggableQsPanel extends QSPanel implements OnTouchListener {
public DraggableQsPanel(Context context) {
super(context);
mBrightnessView.setVisibility(View.GONE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for (TileRecord r : mRecords) {
new DragHelper(r.tileView, (DraggableTile) r.tile);
r.tileView.setTag(r.tile);
r.tileView.setOnTouchListener(this);
for (int i = 0; i < r.tileView.getChildCount(); i++) {
r.tileView.getChildAt(i).setClickable(false);
}
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
String tileSpec = (String) ((DraggableTile) v.getTag()).mSpec;
ClipData data = ClipData.newPlainText(tileSpec, tileSpec);
v.startDrag(data, new View.DragShadowBuilder(v), null, 0);
onStartDrag();
return true;
}
return false;
}
}
}

View File

@@ -15,7 +15,10 @@
*/
package com.android.systemui.tuner;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceFragment;
import android.view.MenuItem;
@@ -23,12 +26,25 @@ import com.android.systemui.R;
public class TunerFragment extends PreferenceFragment {
private static final String KEY_QS_TUNER = "qs_tuner";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.tuner_prefs);
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
setHasOptionsMenu(true);
findPreference(KEY_QS_TUNER).setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(android.R.id.content, new QsTuner(), "QsTuner");
ft.addToBackStack(null);
ft.commit();
return false;
}
});
}
@Override