Merge "Adding a customizable development mode tile"

This commit is contained in:
Sunny Goyal
2017-01-05 19:41:15 +00:00
committed by Android (Google) Code Review
13 changed files with 601 additions and 223 deletions

View File

@@ -3467,25 +3467,24 @@
<!-- Quick Settings tiles for Developer Options --> <!-- Quick Settings tiles for Developer Options -->
<service <service
android:name=".qstile.DevelopmentTiles$ShowLayout" android:name=".qstile.DevelopmentModeTile"
android:label="@string/debug_layout" android:label="@string/developer_tile"
android:icon="@drawable/tile_icon_show_layout" android:icon="@drawable/ic_settings_development"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:enabled="false"> android:enabled="true">
<intent-filter> <intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter> </intent-filter>
</service> </service>
<service <activity
android:name=".qstile.DevelopmentTiles$GPUProfiling" android:name=".qstile.DevelopmentTileConfigActivity"
android:label="@string/track_frame_time" android:excludeFromRecents="true"
android:icon="@drawable/tile_icon_graphics" android:launchMode="singleInstance"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" android:label="@string/developer_tile">
android:enabled="false">
<intent-filter> <intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter> </intent-filter>
</service> </activity>
<activity android:name=".HelpTrampoline" <activity android:name=".HelpTrampoline"
android:exported="true" android:exported="true"

View File

@@ -1,52 +0,0 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M23,1v22H1V1H23 M24,0H0v24h24V0L24,0z"/>
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="0.667"
android:pathData="M5,19L1,23"/>
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="0.667"
android:pathData="M1,1L5,5"/>
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="0.667"
android:pathData="M19,5L23,1"/>
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="0.667"
android:pathData="M19,19L23,23"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M14,3.8l-4,0l-0.2,-0.8l4.4,0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M18.8,5.2v13.5H5.2V5.2H18.8 M19.5,4.5h-15v15h15V4.5L19.5,4.5z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.5,9.5h5v11h-5z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.5,16.5h5v5h-5z"/>
</vector>

View File

@@ -1,60 +0,0 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,7.2c-3.2,0 -5.9,2 -7,4.8c1.1,2.8 3.8,4.8 7,4.8s5.9,-2 7,-4.8C17.9,9.2 15.2,7.2 12,7.2zM12,15.2c-1.8,0 -3.2,-1.4 -3.2,-3.2s1.4,-3.2 3.2,-3.2s3.2,1.4 3.2,3.2S13.8,15.2 12,15.2zM12,10.1c-1.1,0 -1.9,0.9 -1.9,1.9s0.9,1.9 1.9,1.9s1.9,-0.9 1.9,-1.9S13.1,10.1 12,10.1z"/>
<path
android:fillColor="#80FFFFFF"
android:pathData="M0.0,6.0h1.0v12.0h-1.0z"/>
<path
android:fillColor="#80FFFFFF"
android:pathData="M6.0,0.0h12.0v1.0h-12.0z"/>
<path
android:fillColor="#80FFFFFF"
android:pathData="M23.0,6.0h1.0v12.0h-1.0z"/>
<path
android:fillColor="#80FFFFFF"
android:pathData="M6.0,23.0h12.0v1.0h-12.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M1.0,23.0l0.0,-5.0l-1.0,0.0l0.0,6.0l6.0,0.0l0.0,-1.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M1.0,1.0l5.0,0.0l0.0,-1.0l-6.0,0.0l0.0,6.0l1.0,0.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M18.0,0.0l0.0,1.0l5.0,0.0l0.0,5.0l1.0,0.0l0.0,-6.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M23.0,23.0l-5.0,0.0l0.0,1.0l6.0,0.0l0.0,-6.0l-1.0,0.0z"/>
<path
android:fillColor="#80FFFFFF"
android:pathData="M9.5,6.0h5.0v1.0h-5.0z"/>
<path
android:fillColor="#80FFFFFF"
android:pathData="M9.5,17.0h5.0v1.0h-5.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4.5,7.0l5.0,0.0l0.0,-1.0l-6.0,0.0l0.0,6.0l0.0,6.0l6.0,0.0l0.0,-1.0l-5.0,0.0l0.0,-5.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M14.5,6.0l0.0,1.0l5.0,0.0l0.0,5.0l0.0,5.0l-5.0,0.0l0.0,1.0l6.0,0.0l0.0,-6.0l0.0,-6.0z"/>
</vector>

View File

@@ -0,0 +1,32 @@
<?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
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="@drawable/switchbar_background"
android:text="@string/developer_tile_unavailable"
android:paddingStart="8dip"
android:paddingEnd="8dip"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit ="marquee_forever"
android:textSize="16sp"
android:fontFamily="sans-serif"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center" />

View File

@@ -7680,6 +7680,12 @@
<!-- Title of developer options to set the smallest width of the screen [CHAR LIMIT=60]--> <!-- Title of developer options to set the smallest width of the screen [CHAR LIMIT=60]-->
<string name="developer_smallest_width">Smallest width</string> <string name="developer_smallest_width">Smallest width</string>
<!-- Title of developer options time [CHAR LIMIT=60]-->
<string name="developer_tile">Development mode</string>
<!-- Message shown when there is no active state for development mode [CHAR LIMIT=NONE]-->
<string name="developer_tile_unavailable">No active state selected</string>
<!-- Message shown when there are no premium SMS apps [CHAR LIMIT=NONE] --> <!-- Message shown when there are no premium SMS apps [CHAR LIMIT=NONE] -->
<string name="premium_sms_none">No installed apps have requested Premium SMS access</string> <string name="premium_sms_none">No installed apps have requested Premium SMS access</string>

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
android:key="debug_input_category"
android:title="@string/debug_input_category">
<SwitchPreference
android:key="show_touches"
android:title="@string/show_touches"
android:summary="@string/show_touches_summary"/>
<SwitchPreference
android:key="pointer_location"
android:title="@string/pointer_location"
android:summary="@string/pointer_location_summary"/>
</PreferenceCategory>
<PreferenceCategory
android:key="debug_drawing_category"
android:title="@string/debug_drawing_category">
<SwitchPreference
android:key="debug_layout"
android:title="@string/debug_layout"
android:summary="@string/debug_layout_summary"/>
<SwitchPreference
android:key="force_rtl_layout_all_locales"
android:title="@string/force_rtl_layout_all_locales"
android:summary="@string/force_rtl_layout_all_locales_summary"/>
<ListPreference
android:key="window_animation_scale"
android:defaultValue="1"
android:summary="%s"
android:title="@string/window_animation_scale_title"
android:entries="@array/window_animation_scale_entries"
android:entryValues="@array/window_animation_scale_values" />
<ListPreference
android:key="transition_animation_scale"
android:defaultValue="1"
android:summary="%s"
android:title="@string/transition_animation_scale_title"
android:entries="@array/transition_animation_scale_entries"
android:entryValues="@array/transition_animation_scale_values" />
<ListPreference
android:key="animator_duration_scale"
android:defaultValue="1"
android:summary="%s"
android:title="@string/animator_duration_scale_title"
android:entries="@array/animator_duration_scale_entries"
android:entryValues="@array/animator_duration_scale_values" />
</PreferenceCategory>
<PreferenceCategory
android:key="debug_hw_drawing_category"
android:title="@string/debug_hw_drawing_category">
<SwitchPreference
android:key="show_hw_screen_udpates"
android:title="@string/show_hw_screen_updates"
android:summary="@string/show_hw_screen_updates_summary"/>
<SwitchPreference
android:key="show_hw_layers_udpates"
android:title="@string/show_hw_layers_updates"
android:summary="@string/show_hw_layers_updates_summary"/>
<ListPreference
android:key="debug_hw_overdraw"
android:defaultValue="false"
android:summary="%s"
android:title="@string/debug_hw_overdraw"
android:entries="@array/debug_hw_overdraw_entries"
android:entryValues="@array/debug_hw_overdraw_values" />
</PreferenceCategory>
<PreferenceCategory
android:key="debug_monitoring_category"
android:title="@string/debug_monitoring_category">
<ListPreference
android:key="track_frame_time"
android:defaultValue="false"
android:summary="%s"
android:title="@string/track_frame_time"
android:entries="@array/track_frame_time_entries"
android:entryValues="@array/track_frame_time_values" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -61,7 +61,7 @@ import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.DashboardSummary; import com.android.settings.dashboard.DashboardSummary;
import com.android.settings.dashboard.SearchResultsSummary; import com.android.settings.dashboard.SearchResultsSummary;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.qstile.DevelopmentTiles; import com.android.settings.qstile.DevelopmentModeTile;
import com.android.settings.search.DynamicIndexableContentMonitor; import com.android.settings.search.DynamicIndexableContentMonitor;
import com.android.settings.search.Index; import com.android.settings.search.Index;
import com.android.settings.search2.SearchFeatureProvider; import com.android.settings.search2.SearchFeatureProvider;
@@ -994,7 +994,7 @@ public class SettingsActivity extends SettingsDrawerActivity
showDev, isAdmin, pm); showDev, isAdmin, pm);
// Reveal development-only quick settings tiles // Reveal development-only quick settings tiles
DevelopmentTiles.setTilesEnabled(this, showDev); setTileEnabled(new ComponentName(this, DevelopmentModeTile.class), showDev);
if (UserHandle.MU_ENABLED && !isAdmin) { if (UserHandle.MU_ENABLED && !isAdmin) {
// When on restricted users, disable all extra categories (but only the settings ones). // When on restricted users, disable all extra categories (but only the settings ones).

View File

@@ -0,0 +1,205 @@
/*
* 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
*/
package com.android.settings.qstile;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.view.IWindowManager;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.WindowManagerGlobal;
import com.android.internal.app.LocalePicker;
import com.android.settings.DevelopmentSettings;
import java.util.Map;
public class DevelopmentModeTile extends TileService {
static final String SHARED_PREFERENCES_NAME = "development_mode_tile_settings";
private static final String SHOW_TOUCHES_KEY = "show_touches";
private static final String POINTER_LOCATION_KEY = "pointer_location";
private static final String DEBUG_LAYOUT_KEY = "debug_layout";
private static final String FORCE_RTL_LAYOUT_KEY = "force_rtl_layout_all_locales";
private static final String WINDOW_ANIMATION_SCALE_KEY = "window_animation_scale";
private static final String TRANSITION_ANIMATION_SCALE_KEY = "transition_animation_scale";
private static final String ANIMATOR_DURATION_SCALE_KEY = "animator_duration_scale";
private static final String SHOW_HW_SCREEN_UPDATES_KEY = "show_hw_screen_udpates";
private static final String SHOW_HW_LAYERS_UPDATES_KEY = "show_hw_layers_udpates";
private static final String DEBUG_HW_OVERDRAW_KEY = "debug_hw_overdraw";
private static final String TRACK_FRAME_TIME_KEY = "track_frame_time";
private DevModeProperties mProps = new DevModeProperties();
@Override
public void onStartListening() {
super.onStartListening();
refresh();
}
public void refresh() {
mProps.refreshState(this);
getQsTile().setState(mProps.isSet ? (mProps.allMatch
? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE) : Tile.STATE_UNAVAILABLE);
getQsTile().updateTile();
}
@Override
public void onClick() {
if (getQsTile().getState() == Tile.STATE_UNAVAILABLE) {
startActivityAndCollapse(new Intent(this, DevelopmentTileConfigActivity.class));
return;
}
boolean active = getQsTile().getState() == Tile.STATE_INACTIVE;
Map<String, ?> values =
getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE).getAll();
ContentResolver cr = getContentResolver();
for (Property prop : mProps.mSysProps) {
Object expected = values.get(prop.prefKey);
String value = active && !prop.isDefault(expected) ? expected.toString() : "false";
SystemProperties.set(prop.key, value);
}
for (Property prop : mProps.mSysSettings) {
boolean expectedTrue = active && !prop.isDefault(values.get(prop.prefKey));
Settings.System.putInt(cr, prop.key, expectedTrue ? 1 : 0);
}
boolean expectedGlobPropTrue = active &&
!mProps.mGlobProp.isDefault(values.get(mProps.mGlobProp.prefKey));
Settings.Global.putInt(cr, mProps.mGlobProp.key, expectedGlobPropTrue ? 1 : 0);
SystemProperties.set(mProps.mGlobProp.key, expectedGlobPropTrue ? "1" : "0");
LocalePicker.updateLocales(getResources().getConfiguration().getLocales());
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
// Update the various animation scale values to expected values or 1. mProps.mAnimScales
// is an ordered array, where the index corresponds to the individual property.
for (int i = 0; i < mProps.mAnimScales.length; i++) {
Object expected = values.get(mProps.mAnimScales[i]);
float expectedFloat = active && expected != null ?
Float.parseFloat(expected.toString()) : 1;
wm.setAnimationScale(i, expectedFloat);
}
} catch (RemoteException e) { }
new DevelopmentSettings.SystemPropPoker().execute(); // Settings app magic
refresh();
}
static class DevModeProperties {
private final Property[] mSysProps = new Property[] {
new Property(View.DEBUG_LAYOUT_PROPERTY, DEBUG_LAYOUT_KEY),
new Property(ThreadedRenderer.DEBUG_DIRTY_REGIONS_PROPERTY,
SHOW_HW_SCREEN_UPDATES_KEY),
new Property(ThreadedRenderer.DEBUG_SHOW_LAYERS_UPDATES_PROPERTY,
SHOW_HW_LAYERS_UPDATES_KEY),
new Property(ThreadedRenderer.DEBUG_OVERDRAW_PROPERTY, DEBUG_HW_OVERDRAW_KEY),
new Property(ThreadedRenderer.PROFILE_PROPERTY, TRACK_FRAME_TIME_KEY),
};
private final Property[] mSysSettings = new Property[] {
new Property(Settings.System.SHOW_TOUCHES, SHOW_TOUCHES_KEY),
new Property(Settings.System.POINTER_LOCATION, POINTER_LOCATION_KEY),
};
private final Property mGlobProp =
new Property(Settings.Global.DEVELOPMENT_FORCE_RTL, FORCE_RTL_LAYOUT_KEY);
private final String[] mAnimScales = new String[] {
WINDOW_ANIMATION_SCALE_KEY,
TRANSITION_ANIMATION_SCALE_KEY,
ANIMATOR_DURATION_SCALE_KEY
};
/**
* True is the values of all the properties corresponds to the expected values. Updated when
* {@link #refreshState(Context)} is called.
*/
public boolean allMatch;
/**
* True is at least one property has a non-default expected value. Updated when
* {@link #refreshState(Context)} is called. Not that if all properties have default
* expected value, then active and non-active state will be the same.
*/
public boolean isSet;
public void refreshState(Context context) {
Map<String, ?> values =
context.getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE).getAll();
allMatch = true;
// True if there is at least one non-default value.
isSet = false;
for (Property prop : mSysProps) {
Object expected = values.get(prop.prefKey);
String actual = SystemProperties.get(prop.key);
allMatch &= prop.isDefault(expected)
? prop.isDefault(actual) : expected.toString().equals(actual);
isSet |= !prop.isDefault(expected);
}
ContentResolver cr = context.getContentResolver();
for (Property prop : mSysSettings) {
boolean expectedTrue = !prop.isDefault(values.get(prop.prefKey));
isSet |= expectedTrue;
allMatch &= expectedTrue == (Settings.System.getInt(cr, prop.key, 0) != 0);
}
boolean expectedGlopPropTrue = !mGlobProp.isDefault(values.get(mGlobProp.prefKey));
isSet |= expectedGlopPropTrue;
allMatch &= expectedGlopPropTrue == (Settings.Global.getInt(cr, mGlobProp.key, 0) != 0);
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
for (int i = 0; i < mAnimScales.length; i++) {
Object expected = values.get(mAnimScales[i]);
float expectedFloat = expected == null
? 1 : Float.parseFloat(expected.toString());
isSet |= expectedFloat != 1;
allMatch &= expectedFloat == wm.getAnimationScale(i);
}
} catch (RemoteException e) { }
}
}
private static class Property {
final String key;
final String prefKey;
Property(String key, String prefKey) {
this.key = key;
this.prefKey = prefKey;
}
boolean isDefault(Object value) {
if (value == null) {
return true;
}
String str = value.toString();
return str.equals("") || str.equals("false");
}
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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
*/
package com.android.settings.qstile;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.instrumentation.Instrumentable;
import com.android.settings.widget.SwitchBar;
public class DevelopmentTileConfigActivity extends SettingsActivity {
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent())
.putExtra(EXTRA_SHOW_FRAGMENT, DevelopmentTileConfigFragment.class.getName())
.putExtra(EXTRA_HIDE_DRAWER, true);
return modIntent;
}
@Override
protected boolean isValidFragment(String fragmentName) {
return (DevelopmentTileConfigFragment.class.getName().equals(fragmentName));
}
public static class DevelopmentTileConfigFragment extends SettingsPreferenceFragment implements
SharedPreferences.OnSharedPreferenceChangeListener {
private DevelopmentModeTile.DevModeProperties mProps =
new DevelopmentModeTile.DevModeProperties();
private SwitchBar mSwitchBar;
private View mDisabledMessage;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
getPreferenceManager()
.setSharedPreferencesName(DevelopmentModeTile.SHARED_PREFERENCES_NAME);
addPreferencesFromResource(R.xml.development_tile_prefs);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mDisabledMessage = setPinnedHeaderView(R.layout.development_tile_config_header);
refreshHeader();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar();
mSwitchBar.setEnabled(false);
}
@Override
public int getMetricsCategory() {
return Instrumentable.METRICS_CATEGORY_UNKNOWN;
}
@Override
public void onResume() {
super.onResume();
refreshHeader();
getPreferenceManager().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceManager().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
refreshHeader();
}
private void refreshHeader() {
if (mSwitchBar != null && mDisabledMessage != null) {
mProps.refreshState(getActivity());
if (mProps.isSet) {
mSwitchBar.show();
mDisabledMessage.setVisibility(View.GONE);
} else {
mSwitchBar.hide();
mDisabledMessage.setVisibility(View.VISIBLE);
}
mSwitchBar.setChecked(mProps.allMatch);
}
}
}
}

View File

@@ -1,95 +0,0 @@
/*
* 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.
*/
package com.android.settings.qstile;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.SystemProperties;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.view.ThreadedRenderer;
import android.view.View;
import com.android.settings.DevelopmentSettings;
public class DevelopmentTiles {
// List of components that need to be enabled when developer tools are turned on
static final Class[] TILE_CLASSES = new Class[] {
ShowLayout.class,
GPUProfiling.class,
};
public static void setTilesEnabled(Context context, boolean enable) {
final PackageManager pm = context.getPackageManager();
for (Class cls : TILE_CLASSES) {
pm.setComponentEnabledSetting(new ComponentName(context, cls),
enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP);
}
}
/**
* Tile to control the "Show layout bounds" developer setting
*/
public static class ShowLayout extends TileService {
@Override
public void onStartListening() {
super.onStartListening();
refresh();
}
public void refresh() {
final boolean enabled = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
getQsTile().setState(enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
getQsTile().updateTile();
}
@Override
public void onClick() {
SystemProperties.set(View.DEBUG_LAYOUT_PROPERTY,
getQsTile().getState() == Tile.STATE_INACTIVE ? "true" : "false");
new DevelopmentSettings.SystemPropPoker().execute(); // Settings app magic
refresh();
}
}
/**
* Tile to control the "GPU profiling" developer setting
*/
public static class GPUProfiling extends TileService {
@Override
public void onStartListening() {
super.onStartListening();
refresh();
}
public void refresh() {
final String value = SystemProperties.get(ThreadedRenderer.PROFILE_PROPERTY);
getQsTile().setState(value.equals("visual_bars")
? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
getQsTile().updateTile();
}
@Override
public void onClick() {
SystemProperties.set(ThreadedRenderer.PROFILE_PROPERTY,
getQsTile().getState() == Tile.STATE_INACTIVE ? "visual_bars" : "");
new DevelopmentSettings.SystemPropPoker().execute(); // Settings app magic
refresh();
}
}
}

View File

@@ -89,3 +89,4 @@ com.android.settings.notification.ZenModeSettings
com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment
com.android.settings.applications.ConvertToFbe com.android.settings.applications.ConvertToFbe
com.android.settings.localepicker.LocaleListEditor com.android.settings.localepicker.LocaleListEditor
com.android.settings.qstile.DevelopmentTileConfigActivity$DevelopmentTileConfigFragment

View File

@@ -0,0 +1,75 @@
package com.android.settings.qstile;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.service.quicksettings.Tile;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.shadow.ShadowTileService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.internal.ShadowExtractor;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
shadows = ShadowTileService.class)
public class DevelopmentModeTileTest {
@Mock private Tile mTile;
@Mock private DevelopmentModeTile.DevModeProperties mProps;
private DevelopmentModeTile mDevelopmentModeTile;
private ShadowTileService mShadowTileService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mDevelopmentModeTile = Robolectric.buildService(DevelopmentModeTile.class).get();
ReflectionHelpers.setField(mDevelopmentModeTile, "mProps", mProps);
mShadowTileService = (ShadowTileService) ShadowExtractor.extract(mDevelopmentModeTile);
mShadowTileService.setTile(mTile);
}
@Test
public void refresh() {
verifyRefreshState(false, true, Tile.STATE_UNAVAILABLE);
verifyRefreshState(false, false, Tile.STATE_UNAVAILABLE);
verifyRefreshState(true, false, Tile.STATE_INACTIVE);
verifyRefreshState(true, true, Tile.STATE_ACTIVE);
}
@Test
public void onClick_startSetting() {
when(mTile.getState()).thenReturn(Tile.STATE_UNAVAILABLE);
mDevelopmentModeTile.onClick();
Intent intent = mShadowTileService.getNextStartedActivity();
assertEquals(DevelopmentTileConfigActivity.class.getName(),
intent.getComponent().getClassName());
}
private void verifyRefreshState(boolean isSet, boolean allMatch, int expectedState) {
reset(mProps, mTile);
mProps.isSet = isSet;
mProps.allMatch = allMatch;
mDevelopmentModeTile.refresh();
verify(mProps).refreshState(eq(mDevelopmentModeTile));
verify(mTile).setState(eq(expectedState));
}
}

View File

@@ -0,0 +1,38 @@
package com.android.settings.testutils.shadow;
import android.content.Intent;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadows.ShadowService;
/**
* Shadow for {@link TileService}.
*/
@Implements(TileService.class)
public class ShadowTileService extends ShadowService {
@RealObject TileService realService;
private Tile mTile;
public void __constructor__() { }
@Implementation
public final Tile getQsTile() {
return mTile;
}
@Implementation
public final void startActivityAndCollapse(Intent intent) {
realService.startActivity(intent);
}
// Non-Android setter.
public void setTile(Tile tile) {
mTile = tile;
}
}