Add conditionals to Settings
Also add Airplane Mode and Hotspot conditionals (more to come soon) Change-Id: I11f206db59f7c715f416fb5852b8f0fcb857a247
This commit is contained in:
@@ -2656,5 +2656,22 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Conditional receivers, only enabled during silenced state, default off-->
|
||||
<receiver
|
||||
android:name=".dashboard.conditional.HotspotCondition$Receiver"
|
||||
android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".dashboard.conditional.AirplaneModeCondition$Receiver"
|
||||
android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.AIRPLANE_MODE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
57
res/drawable/ic_airplane.xml
Normal file
57
res/drawable/ic_airplane.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="root"
|
||||
android:alpha="1.0"
|
||||
android:height="48dp"
|
||||
android:width="48dp"
|
||||
android:viewportHeight="48"
|
||||
android:viewportWidth="48" >
|
||||
<group
|
||||
android:name="ic_signal_airplane"
|
||||
android:translateX="21.9995"
|
||||
android:translateY="25.73401" >
|
||||
<group
|
||||
android:name="ic_signal_airplane_pivot"
|
||||
android:translateX="-23.21545"
|
||||
android:translateY="-18.86649" >
|
||||
<clip-path
|
||||
android:name="mask"
|
||||
android:pathData="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
|
||||
<group
|
||||
android:name="cross" >
|
||||
<path
|
||||
android:name="cross_1"
|
||||
android:pathData="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeAlpha="0"
|
||||
android:strokeWidth="3.5"
|
||||
android:fillColor="#00000000" />
|
||||
</group>
|
||||
<group
|
||||
android:name="plane"
|
||||
android:translateX="23.481"
|
||||
android:translateY="18.71151" >
|
||||
<path
|
||||
android:name="plane_1"
|
||||
android:pathData="M 18.9439849854,7.98849487305 c 0.0,0.0 0.0,-4.0 0.0,-4.0 c 0.0,0.0 -16.0,-10.0 -16.0,-10.0 c 0.0,0.0 0.0,-11.0 0.0,-11.0 c 0.0,-1.70001220703 -1.30000305176,-3.0 -3.0,-3.0 c -1.69999694824,0.0 -3.0,1.29998779297 -3.0,3.0 c 0.0,0.0 0.0,11.0 0.0,11.0 c 0.0,0.0 -16.0,10.0 -16.0,10.0 c 0.0,0.0 0.0,4.0 0.0,4.0 c 0.0,0.0 16.0,-5.0 16.0,-5.0 c 0.0,0.0 0.0,11.0 0.0,11.0 c 0.0,0.0 -4.0,3.0 -4.0,3.0 c 0.0,0.0 0.0,3.0 0.0,3.0 c 0.0,0.0 7.0,-2.0 7.0,-2.0 c 0.0,0.0 7.0,2.0 7.0,2.0 c 0.0,0.0 0.0,-3.0 0.0,-3.0 c 0.0,0.0 -4.0,-3.0 -4.0,-3.0 c 0.0,0.0 0.0,-11.0 0.0,-11.0 c 0.0,0.0 16.0,5.0 16.0,5.0 Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillAlpha="1" />
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
@@ -19,10 +19,11 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
|
||||
<path
|
||||
android:fillColor="?android:attr/colorControlNormal"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12.0,8.0l-6.0,6.0 1.41,1.41L12.0,10.83l4.59,4.58L18.0,14.0z"/>
|
||||
|
||||
</vector>
|
||||
|
@@ -19,10 +19,11 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
|
||||
<path
|
||||
android:fillColor="?android:attr/colorControlNormal"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
|
||||
|
||||
</vector>
|
||||
|
62
res/drawable/ic_hotspot.xml
Normal file
62
res/drawable/ic_hotspot.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="root"
|
||||
android:alpha="1.0"
|
||||
android:height="48dp"
|
||||
android:width="48dp"
|
||||
android:viewportHeight="48"
|
||||
android:viewportWidth="48" >
|
||||
<group
|
||||
android:name="ic_hotspot"
|
||||
android:translateX="23.9778"
|
||||
android:translateY="24.26443" >
|
||||
<group
|
||||
android:name="ic_hotspot_pivot"
|
||||
android:translateX="-23.21545"
|
||||
android:translateY="-18.86649" >
|
||||
<clip-path
|
||||
android:name="mask"
|
||||
android:pathData="M 38.8337860107,-40.3974914551 c 0.0,0.0 -38.4077911377,30.8523712158 -38.4077911377,30.8523712158 c 0.0,0.0 6.97125244141,7.33258056641 6.97125244141,7.33258056641 c 0.0,0.0 -2.4169921875,2.57838439941 -2.4169921875,2.57838439941 c 0.0,0.0 -6.77128601074,-6.82850646973 -6.77128601074,-6.82850646973 c 0.0,0.0 -32.6199798584,25.1699066162 -32.6199798584,25.1699066162 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 27.6589050293,-22.6579437256 27.6589050293,-22.6579437256 c 0.0,0.0 -30.8645172119,-34.00390625 -30.8645172119,-34.00390625 c 0.0,0.0 2.70756530762,-1.99278259277 2.70756530762,-1.99278259277 c 0.0,0.0 1.53030395508,-0.876571655273 1.53030395508,-0.876571655274 c 0.0,0.0 2.85780334473,-3.12069702148 2.85780334473,-3.12069702148 c 0.0,0.0 0.659332275391,0.664688110352 0.659332275391,0.664688110351 c 0.0,0.0 -3.13299560547,2.82977294922 -3.13299560547,2.82977294922 c 0.0,0.0 29.0108337402,34.4080963135 29.0108337402,34.4080963135 c 0.0,0.0 42.8175811768,-34.3554534912 42.8175811768,-34.3554534912 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
|
||||
<group
|
||||
android:name="cross" >
|
||||
<path
|
||||
android:name="cross_1"
|
||||
android:pathData="M 4.44044494629,2.24310302734 c 0.0,0.0 0.0875396728516,0.112457275391 0.0875396728516,0.112457275391 "
|
||||
android:strokeColor="#FFFFFFFF"
|
||||
android:strokeAlpha="0"
|
||||
android:strokeWidth="3.5"
|
||||
android:fillColor="#00000000" />
|
||||
</group>
|
||||
<group
|
||||
android:name="hotspot"
|
||||
android:translateX="23.481"
|
||||
android:translateY="18.71151" >
|
||||
<group
|
||||
android:name="circles"
|
||||
android:translateX="-0.23909"
|
||||
android:translateY="-0.10807" >
|
||||
<path
|
||||
android:name="path_3"
|
||||
android:pathData="M -0.0042724609375,-2.64895629883 c -2.20922851562,0.0 -4.0,1.791015625 -4.0,4.0 c 0.0,2.20922851562 1.79077148438,4.0 4.0,4.0 c 2.208984375,0.0 4.0,-1.79077148438 4.0,-4.0 c 0.0,-2.208984375 -1.791015625,-4.0 -4.0,-4.0 Z M 11.9957275391,1.35104370117 c 0.0,-6.626953125 -5.373046875,-12.0 -12.0,-12.0 c -6.62719726562,0.0 -12.0,5.373046875 -12.0,12.0 c 0.0,4.43603515625 2.41381835938,8.30004882812 5.99194335938,10.3771972656 c 0.0,0.0 2.01586914062,-3.48217773438 2.01586914062,-3.48217773438 c -2.38500976562,-1.38500976562 -4.0078125,-3.93798828125 -4.0078125,-6.89501953125 c 0.0,-4.41796875 3.58178710938,-8.0 8.0,-8.0 c 4.41796875,0.0 8.0,3.58203125 8.0,8.0 c 0.0,2.95703125 -1.623046875,5.51000976562 -4.00805664062,6.89501953125 c 0.0,0.0 2.01586914062,3.48217773438 2.01586914062,3.48217773438 c 3.578125,-2.0771484375 5.9921875,-5.94116210938 5.9921875,-10.3771972656 Z M -0.0042724609375,-18.6489562988 c -11.0451660156,0.0 -20.0,8.9541015625 -20.0,20.0 c 0.0,7.39306640625 4.02099609375,13.8330078125 9.98779296875,17.2951660156 c 0.0,0.0 2.00219726562,-3.458984375 2.00219726562,-3.458984375 c -4.77319335938,-2.77001953125 -7.98999023438,-7.92211914062 -7.98999023438,-13.8361816406 c 0.0,-8.8369140625 7.16381835938,-16.0 16.0,-16.0 c 8.83595275879,0.0 16.0000152588,7.1630859375 16.0000152588,16.0 c 0.0,5.9140625 -3.21704101562,11.0661621094 -7.990234375,13.8361816406 c 0.0,0.0 2.00219726562,3.458984375 2.00219726563,3.458984375 c 5.966796875,-3.46215820312 9.98803710937,-9.90209960938 9.98803710938,-17.2951660156 c 0.0,-11.0458984375 -8.955078125,-20.0 -20.0000152588,-20.0 Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:fillAlpha="1" />
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
109
res/layout/condition_card.xml
Normal file
109
res/layout/condition_card.xml
Normal file
@@ -0,0 +1,109 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:background="?android:attr/colorAccent"
|
||||
android:elevation="3dp"
|
||||
android:clickable="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/collapsed_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="36dp"
|
||||
android:tint="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/expand_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:tint="?android:attr/textColorPrimaryInverse"
|
||||
android:padding="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackground" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/detail_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="60dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingBottom="16dp"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<!-- TODO: Better background -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height=".25dp"
|
||||
android:background="@android:color/white" />
|
||||
|
||||
<com.android.internal.widget.ButtonBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
style="?attr/buttonBarStyle"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||
|
||||
<Button
|
||||
android:id="@+id/first_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="0dp"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/second_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
|
||||
</com.android.internal.widget.ButtonBarLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<com.android.settings.dashboard.conditional.FocusRecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/dashboard_container"
|
||||
android:layout_width="match_parent"
|
||||
|
@@ -101,7 +101,7 @@
|
||||
<dimen name="dashboard_category_padding_end">0dp</dimen>
|
||||
|
||||
<!-- Dashboard category panel elevation -->
|
||||
<dimen name="dashboard_category_elevation">4dp</dimen>
|
||||
<dimen name="dashboard_category_elevation">2dp</dimen>
|
||||
|
||||
<!-- Dashboard category title layout height -->
|
||||
<dimen name="dashboard_category_title_height">48dp</dimen>
|
||||
|
@@ -6691,4 +6691,20 @@
|
||||
<string name="default_organisation_name">organisation</string>
|
||||
<string name="default_admin_support_msg">Contact them to learn more.</string>
|
||||
<string name="list_of_administrators">List of administrators</string>
|
||||
|
||||
<!-- Turn off a conditional state of the device (e.g. airplane mode, or hotspot) [CHAR LIMIT=30] -->
|
||||
<string name="condition_turn_off">Turn off</string>
|
||||
|
||||
<!-- Title of condition that hotspot is on [CHAR LIMIT=30] -->
|
||||
<string name="condition_hotspot_title">Hotspot is on</string>
|
||||
|
||||
<!-- Summary of condition that hotspot is on [CHAR LIMIT=NONE] -->
|
||||
<string name="condition_hotspot_summary">Portable Wi-Fi hotspot <xliff:g name="ap_name" example="AndroidAP">%1$s</xliff:g> is active, Wi-Fi for this device is turned off.</string>
|
||||
|
||||
<!-- Title of condition that airplane mode is on [CHAR LIMIT=30] -->
|
||||
<string name="condition_airplane_title">Airplane mode is on</string>
|
||||
|
||||
<!-- Summary of condition that airplane mode is on [CHAR LIMIT=NONE] -->
|
||||
<string name="condition_airplane_summary">Wi-Fi, Bluetooth, and cellular network are turned off. You can\'t make phone calls or connect to the Internet.</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -74,6 +74,7 @@ import android.text.style.TtsSpan;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -83,8 +84,6 @@ import android.view.animation.AnimationUtils;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TabWidget;
|
||||
import com.android.internal.util.UserIcons;
|
||||
import com.android.settingslib.drawer.UserAdapter;
|
||||
import com.android.settingslib.drawer.UserAdapter.UserDetails;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -1058,5 +1057,11 @@ public final class Utils {
|
||||
return UserHandle.myUserId();
|
||||
}
|
||||
}
|
||||
|
||||
public static int resolveResource(Context context, int attr) {
|
||||
TypedValue value = new TypedValue();
|
||||
context.getTheme().resolveAttribute(attr, value, true);
|
||||
return value.resourceId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,6 @@ package com.android.settings.dashboard;
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -26,40 +25,52 @@ import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.dashboard.conditional.Condition;
|
||||
import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.DashboardTile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder> {
|
||||
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder> implements View.OnClickListener {
|
||||
public static final String TAG = "DashboardAdapter";
|
||||
|
||||
private final List<Object> mItems = new ArrayList<>();
|
||||
private final List<Integer> mTypes = new ArrayList<>();
|
||||
private final List<Integer> mIds = new ArrayList<>();
|
||||
|
||||
private final List<DashboardCategory> mCategories;
|
||||
private final Context mContext;
|
||||
|
||||
private List<DashboardCategory> mCategories;
|
||||
private List<Condition> mConditions;
|
||||
|
||||
private boolean mIsShowingAll;
|
||||
// Used for counting items;
|
||||
private int mId;
|
||||
|
||||
public DashboardAdapter(Context context, List<DashboardCategory> categories) {
|
||||
private Condition mExpandedCondition = null;
|
||||
|
||||
public DashboardAdapter(Context context) {
|
||||
mContext = context;
|
||||
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setCategories(List<DashboardCategory> categories) {
|
||||
mCategories = categories;
|
||||
|
||||
// TODO: Better place for tinting?
|
||||
TypedValue tintColor = new TypedValue();
|
||||
context.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
|
||||
mContext.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
|
||||
tintColor, true);
|
||||
for (int i = 0; i < categories.size(); i++) {
|
||||
for (int j = 0; j < categories.get(i).tiles.size(); j++) {
|
||||
DashboardTile tile = categories.get(i).tiles.get(j);
|
||||
|
||||
if (!context.getPackageName().equals(
|
||||
if (!mContext.getPackageName().equals(
|
||||
tile.intent.getComponent().getPackageName())) {
|
||||
// If this drawable is coming from outside Settings, tint it to match the
|
||||
// color.
|
||||
@@ -67,9 +78,12 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
}
|
||||
}
|
||||
setShowingAll(mIsShowingAll);
|
||||
}
|
||||
|
||||
setShowingAll(false);
|
||||
setHasStableIds(true);
|
||||
public void setConditions(List<Condition> conditions) {
|
||||
mConditions = conditions;
|
||||
setShowingAll(mIsShowingAll);
|
||||
}
|
||||
|
||||
public boolean isShowingAll() {
|
||||
@@ -88,19 +102,21 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
public void setShowingAll(boolean showingAll) {
|
||||
mIsShowingAll = showingAll;
|
||||
reset();
|
||||
countItem(null, com.android.settings.R.layout.dashboard_spacer, true);
|
||||
for (int i = 0; i < mCategories.size(); i++) {
|
||||
for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
|
||||
countItem(mConditions.get(i), R.layout.condition_card, mConditions.get(i).shouldShow());
|
||||
}
|
||||
countItem(null, R.layout.dashboard_spacer, true);
|
||||
for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
|
||||
DashboardCategory category = mCategories.get(i);
|
||||
countItem(category, com.android.settings.R.layout.dashboard_category, mIsShowingAll);
|
||||
countItem(category, R.layout.dashboard_category, mIsShowingAll);
|
||||
for (int j = 0; j < category.tiles.size(); j++) {
|
||||
DashboardTile tile = category.tiles.get(j);
|
||||
Log.d(TAG, "Maybe adding " + tile.intent.getComponent().getClassName());
|
||||
countItem(tile, com.android.settings.R.layout.dashboard_tile, mIsShowingAll
|
||||
countItem(tile, R.layout.dashboard_tile, mIsShowingAll
|
||||
|| ArrayUtils.contains(DashboardSummary.INITIAL_ITEMS,
|
||||
tile.intent.getComponent().getClassName()));
|
||||
}
|
||||
}
|
||||
countItem(null, com.android.settings.R.layout.see_all, true);
|
||||
countItem(null, R.layout.see_all, true);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -129,10 +145,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
@Override
|
||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
||||
switch (mTypes.get(position)) {
|
||||
case com.android.settings.R.layout.dashboard_category:
|
||||
case R.layout.dashboard_category:
|
||||
onBindCategory(holder, (DashboardCategory) mItems.get(position));
|
||||
break;
|
||||
case com.android.settings.R.layout.dashboard_tile:
|
||||
case R.layout.dashboard_tile:
|
||||
final DashboardTile tile = (DashboardTile) mItems.get(position);
|
||||
onBindTile(holder, tile);
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -142,7 +158,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
});
|
||||
break;
|
||||
case com.android.settings.R.layout.see_all:
|
||||
case R.layout.see_all:
|
||||
onBindSeeAll(holder);
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@@ -151,6 +167,16 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
});
|
||||
break;
|
||||
case R.layout.condition_card:
|
||||
ConditionAdapterUtils.bindViews((Condition) mItems.get(position), holder,
|
||||
mItems.get(position) == mExpandedCondition, this,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onExpandClick(v);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +196,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
|
||||
private void onBindSeeAll(DashboardItemHolder holder) {
|
||||
holder.title.setText(mIsShowingAll ? com.android.settings.R.string.see_less
|
||||
: com.android.settings.R.string.see_all);
|
||||
holder.title.setText(mIsShowingAll ? R.string.see_less
|
||||
: R.string.see_all);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,10 +215,38 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
return mIds.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getTag() == mExpandedCondition) {
|
||||
mExpandedCondition.onPrimaryClick();
|
||||
} else {
|
||||
mExpandedCondition = (Condition) v.getTag();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void onExpandClick(View v) {
|
||||
if (v.getTag() == mExpandedCondition) {
|
||||
mExpandedCondition = null;
|
||||
} else {
|
||||
mExpandedCondition = (Condition) v.getTag();
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Object getItem(long itemId) {
|
||||
for (int i = 0; i < mIds.size(); i++) {
|
||||
if (mIds.get(i) == itemId) {
|
||||
return mItems.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
|
||||
private final ImageView icon;
|
||||
private final TextView title;
|
||||
private final TextView summary;
|
||||
public final ImageView icon;
|
||||
public final TextView title;
|
||||
public final TextView summary;
|
||||
|
||||
public DashboardItemHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
@@ -18,7 +18,6 @@ package com.android.settings.dashboard;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -31,13 +30,17 @@ import com.android.settings.InstrumentedFragment;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
|
||||
import com.android.settings.dashboard.conditional.ConditionManager;
|
||||
import com.android.settings.dashboard.conditional.FocusRecyclerView;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.SettingsDrawerActivity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DashboardSummary extends InstrumentedFragment
|
||||
implements SettingsDrawerActivity.CategoryListener {
|
||||
implements SettingsDrawerActivity.CategoryListener, ConditionManager.ConditionListener,
|
||||
FocusRecyclerView.FocusListener {
|
||||
public static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG_TIMING = false;
|
||||
private static final String TAG = "DashboardSummary";
|
||||
@@ -51,11 +54,10 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
Settings.StorageSettingsActivity.class.getName(),
|
||||
};
|
||||
|
||||
private static final int MSG_REBUILD_UI = 1;
|
||||
|
||||
private RecyclerView mDashboard;
|
||||
private FocusRecyclerView mDashboard;
|
||||
private DashboardAdapter mAdapter;
|
||||
private SummaryLoader mSummaryLoader;
|
||||
private ConditionManager mConditionManager;
|
||||
|
||||
@Override
|
||||
protected int getMetricsCategory() {
|
||||
@@ -73,6 +75,7 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
setHasOptionsMenu(true);
|
||||
if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
|
||||
+ " ms");
|
||||
mConditionManager = ConditionManager.get(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,6 +98,7 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
|
||||
((SettingsDrawerActivity) getActivity()).addCategoryListener(this);
|
||||
mSummaryLoader.setListening(true);
|
||||
Log.d(TAG, "onResume");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +109,16 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
mSummaryLoader.setListening(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
if (hasWindowFocus) {
|
||||
mConditionManager.addListener(this);
|
||||
mConditionManager.refreshAll();
|
||||
} else {
|
||||
mConditionManager.remListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -113,11 +127,16 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle bundle) {
|
||||
mDashboard = (RecyclerView) view.findViewById(R.id.dashboard_container);
|
||||
mDashboard = (FocusRecyclerView) view.findViewById(R.id.dashboard_container);
|
||||
LinearLayoutManager llm = new LinearLayoutManager(getContext());
|
||||
llm.setOrientation(LinearLayoutManager.VERTICAL);
|
||||
mDashboard.setLayoutManager(llm);
|
||||
mDashboard.setHasFixedSize(true);
|
||||
mDashboard.setListener(this);
|
||||
mAdapter = new DashboardAdapter(getContext());
|
||||
mAdapter.setConditions(mConditionManager.getConditions());
|
||||
mSummaryLoader.setAdapter(mAdapter);
|
||||
ConditionAdapterUtils.addDismiss(mDashboard);
|
||||
|
||||
rebuildUI();
|
||||
}
|
||||
@@ -132,10 +151,7 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
// TODO: Cache summaries from old categories somehow.
|
||||
List<DashboardCategory> categories =
|
||||
((SettingsActivity) getActivity()).getDashboardCategories();
|
||||
boolean showingAll = mAdapter != null && mAdapter.isShowingAll();
|
||||
mAdapter = new DashboardAdapter(getContext(), categories);
|
||||
mSummaryLoader.setAdapter(mAdapter);
|
||||
mAdapter.setShowingAll(showingAll);
|
||||
mAdapter.setCategories(categories);
|
||||
mDashboard.setAdapter(mAdapter);
|
||||
|
||||
long delta = System.currentTimeMillis() - start;
|
||||
@@ -146,4 +162,10 @@ public class DashboardSummary extends InstrumentedFragment
|
||||
public void onCategoriesChanged() {
|
||||
rebuildUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConditionsChanged() {
|
||||
Log.d(TAG, "onConditionsChanged");
|
||||
mAdapter.setConditions(mConditionManager.getConditions());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.settings.dashboard.conditional;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.ConnectivityManager;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settingslib.WirelessUtils;
|
||||
|
||||
public class AirplaneModeCondition extends Condition {
|
||||
|
||||
public AirplaneModeCondition(ConditionManager conditionManager) {
|
||||
super(conditionManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshState() {
|
||||
setActive(WirelessUtils.isAirplaneModeOn(mManager.getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSilenceChanged(boolean silenced) {
|
||||
// Only need to listen for airplane mode changes when its been silenced.
|
||||
PackageManager pm = mManager.getContext().getPackageManager();
|
||||
pm.setComponentEnabledSetting(new ComponentName(mManager.getContext(), Receiver.class),
|
||||
silenced ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return Icon.createWithResource(mManager.getContext(), R.drawable.ic_airplane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mManager.getContext().getString(R.string.condition_airplane_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mManager.getContext().getString(R.string.condition_airplane_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence[] getActions() {
|
||||
return new CharSequence[] { mManager.getContext().getString(R.string.condition_turn_off) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrimaryClick() {
|
||||
mManager.getContext().startActivity(new Intent(mManager.getContext(),
|
||||
Settings.WirelessSettings.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionClick(int index) {
|
||||
if (index == 0) {
|
||||
ConnectivityManager.from(mManager.getContext()).setAirplaneMode(false);
|
||||
setActive(false);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected index " + index);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Receiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
|
||||
ConditionManager.get(context).getCondition(AirplaneModeCondition.class)
|
||||
.refreshState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
107
src/com/android/settings/dashboard/conditional/Condition.java
Normal file
107
src/com/android/settings/dashboard/conditional/Condition.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.settings.dashboard.conditional;
|
||||
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class Condition {
|
||||
|
||||
private static final String KEY_SILENCE = "silence";
|
||||
private static final String KEY_ACTIVE = "active";
|
||||
private static final String KEY_LAST_STATE = "last_state";
|
||||
|
||||
protected final ConditionManager mManager;
|
||||
|
||||
private boolean mIsSilenced;
|
||||
private boolean mIsActive;
|
||||
private long mLastStateChange;
|
||||
|
||||
public Condition(ConditionManager manager) {
|
||||
mManager = manager;
|
||||
}
|
||||
|
||||
void restoreState(PersistableBundle bundle) {
|
||||
mIsSilenced = bundle.getBoolean(KEY_SILENCE);
|
||||
mIsActive = bundle.getBoolean(KEY_ACTIVE);
|
||||
mLastStateChange = bundle.getLong(KEY_LAST_STATE);
|
||||
}
|
||||
|
||||
void saveState(PersistableBundle bundle) {
|
||||
bundle.putBoolean(KEY_SILENCE, mIsSilenced);
|
||||
bundle.putBoolean(KEY_ACTIVE, mIsActive);
|
||||
bundle.putLong(KEY_LAST_STATE, mLastStateChange);
|
||||
}
|
||||
|
||||
protected void notifyChanged() {
|
||||
mManager.notifyChanged(this);
|
||||
}
|
||||
|
||||
public boolean isSilenced() {
|
||||
return mIsSilenced;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return mIsActive;
|
||||
}
|
||||
|
||||
protected void setActive(boolean active) {
|
||||
if (mIsActive == active) {
|
||||
return;
|
||||
}
|
||||
mIsActive = active;
|
||||
mLastStateChange = System.currentTimeMillis();
|
||||
if (mIsSilenced && !active) {
|
||||
mIsSilenced = false;
|
||||
onSilenceChanged(mIsSilenced);
|
||||
}
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
public void silence() {
|
||||
if (!mIsSilenced) {
|
||||
mIsSilenced = true;
|
||||
onSilenceChanged(mIsSilenced);
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSilenceChanged(boolean state) {
|
||||
// Optional enable/disable receivers based on silence state.
|
||||
}
|
||||
|
||||
public boolean shouldShow() {
|
||||
return isActive() && !isSilenced();
|
||||
}
|
||||
|
||||
long getLastChange() {
|
||||
return mLastStateChange;
|
||||
}
|
||||
|
||||
// State.
|
||||
public abstract void refreshState();
|
||||
|
||||
// UI.
|
||||
public abstract Icon getIcon();
|
||||
public abstract CharSequence getTitle();
|
||||
public abstract CharSequence getSummary();
|
||||
public abstract CharSequence[] getActions();
|
||||
|
||||
public abstract void onPrimaryClick();
|
||||
public abstract void onActionClick(int index);
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.settings.dashboard.conditional;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardAdapter;
|
||||
|
||||
public class ConditionAdapterUtils {
|
||||
|
||||
public static void addDismiss(final RecyclerView recyclerView) {
|
||||
ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0,
|
||||
ItemTouchHelper.START | ItemTouchHelper.END) {
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
||||
RecyclerView.ViewHolder target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
return viewHolder.getItemViewType() == R.layout.condition_card
|
||||
? super.getSwipeDirs(recyclerView, viewHolder) : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
DashboardAdapter adapter = (DashboardAdapter) recyclerView.getAdapter();
|
||||
Object item = adapter.getItem(viewHolder.getItemId());
|
||||
if (item instanceof Condition) {
|
||||
((Condition) item).silence();
|
||||
}
|
||||
}
|
||||
};
|
||||
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
|
||||
itemTouchHelper.attachToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
public static void bindViews(final Condition condition,
|
||||
DashboardAdapter.DashboardItemHolder view, boolean isExpanded,
|
||||
View.OnClickListener onClickListener, View.OnClickListener onExpandListener) {
|
||||
view.itemView.setTag(condition);
|
||||
view.itemView.setOnClickListener(onClickListener);
|
||||
view.icon.setImageIcon(condition.getIcon());
|
||||
view.title.setText(condition.getTitle());
|
||||
ImageView expand = (ImageView) view.itemView.findViewById(R.id.expand_indicator);
|
||||
expand.setTag(condition);
|
||||
expand.setImageResource(isExpanded ? R.drawable.ic_expand_less : R.drawable.ic_expand_more);
|
||||
expand.setOnClickListener(onExpandListener);
|
||||
|
||||
View detailGroup = view.itemView.findViewById(R.id.detail_group);
|
||||
// TODO: Animate expand/collapse
|
||||
detailGroup.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
|
||||
if (isExpanded) {
|
||||
view.summary.setText(condition.getSummary());
|
||||
CharSequence[] actions = condition.getActions();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Button button = (Button) detailGroup.findViewById(i == 0
|
||||
? R.id.first_action : R.id.second_action);
|
||||
if (actions.length > i) {
|
||||
button.setVisibility(View.VISIBLE);
|
||||
button.setText(actions[i]);
|
||||
final int index = i;
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
condition.onActionClick(index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.settings.dashboard.conditional;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ConditionManager {
|
||||
|
||||
private static final String TAG = "ConditionManager";
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private static final String FILE_NAME = "condition_state.xml";
|
||||
private static final String TAG_CONDITIONS = "conditions";
|
||||
private static final String TAG_CONDITION = "condition";
|
||||
private static final String ATTR_CLASS = "class";
|
||||
|
||||
private static ConditionManager sInstance;
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayList<Condition> mConditions;
|
||||
private final File mXmlFile;
|
||||
|
||||
private final ArrayList<ConditionListener> mListeners = new ArrayList<>();
|
||||
|
||||
private ConditionManager(Context context) {
|
||||
mContext = context;
|
||||
mConditions = new ArrayList<Condition>();
|
||||
mXmlFile = new File(context.getFilesDir(), FILE_NAME);
|
||||
if (mXmlFile.exists()) {
|
||||
readFromXml();
|
||||
}
|
||||
addMissingConditions();
|
||||
}
|
||||
|
||||
public void refreshAll() {
|
||||
final int N = mConditions.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
mConditions.get(i).refreshState();
|
||||
}
|
||||
}
|
||||
|
||||
private void readFromXml() {
|
||||
if (DEBUG) Log.d(TAG, "Reading from " + mXmlFile.toString());
|
||||
try {
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
FileReader in = new FileReader(mXmlFile);
|
||||
parser.setInput(in);
|
||||
int state = parser.getEventType();
|
||||
|
||||
while (state != XmlPullParser.END_DOCUMENT) {
|
||||
if (TAG_CONDITION.equals(parser.getName())) {
|
||||
int depth = parser.getDepth();
|
||||
String clz = parser.getAttributeValue("", ATTR_CLASS);
|
||||
Condition condition = createCondition(Class.forName(clz));
|
||||
PersistableBundle bundle = PersistableBundle.restoreFromXml(parser);
|
||||
if (DEBUG) Log.d(TAG, "Reading " + clz + " -- " + bundle);
|
||||
condition.restoreState(bundle);
|
||||
mConditions.add(condition);
|
||||
while (parser.getDepth() > depth) {
|
||||
parser.next();
|
||||
}
|
||||
}
|
||||
state = parser.next();
|
||||
}
|
||||
in.close();
|
||||
} catch (XmlPullParserException | IOException | ClassNotFoundException e) {
|
||||
Log.w(TAG, "Problem reading " + FILE_NAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveToXml() {
|
||||
if (DEBUG) Log.d(TAG, "Writing to " + mXmlFile.toString());
|
||||
try {
|
||||
XmlSerializer serializer = Xml.newSerializer();
|
||||
FileWriter writer = new FileWriter(mXmlFile);
|
||||
serializer.setOutput(writer);
|
||||
|
||||
serializer.startDocument("UTF-8", true);
|
||||
serializer.startTag("", TAG_CONDITIONS);
|
||||
|
||||
final int N = mConditions.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
serializer.startTag("", TAG_CONDITION);
|
||||
serializer.attribute("", ATTR_CLASS, mConditions.get(i).getClass().getName());
|
||||
PersistableBundle bundle = new PersistableBundle();
|
||||
mConditions.get(i).saveState(bundle);
|
||||
bundle.saveToXml(serializer);
|
||||
serializer.endTag("", TAG_CONDITION);
|
||||
}
|
||||
|
||||
serializer.endTag("", TAG_CONDITIONS);
|
||||
serializer.flush();
|
||||
writer.close();
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
Log.w(TAG, "Problem writing " + FILE_NAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMissingConditions() {
|
||||
addIfMissing(AirplaneModeCondition.class);
|
||||
addIfMissing(HotspotCondition.class);
|
||||
}
|
||||
|
||||
private void addIfMissing(Class<? extends Condition> clz) {
|
||||
if (getCondition(clz) == null) {
|
||||
if (DEBUG) Log.d(TAG, "Adding missing " + clz.getName());
|
||||
mConditions.add(createCondition(clz));
|
||||
}
|
||||
}
|
||||
|
||||
private Condition createCondition(Class<?> clz) {
|
||||
if (AirplaneModeCondition.class == clz) {
|
||||
return new AirplaneModeCondition(this);
|
||||
} else if (HotspotCondition.class == clz) {
|
||||
return new HotspotCondition(this);
|
||||
}
|
||||
try {
|
||||
Constructor<?> constructor = clz.getConstructor(ConditionManager.class);
|
||||
return (Condition) constructor.newInstance(this);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException
|
||||
| InvocationTargetException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public <T extends Condition> T getCondition(Class<T> clz) {
|
||||
final int N = mConditions.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (clz.equals(mConditions.get(i).getClass())) {
|
||||
return (T) mConditions.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Condition> getConditions() {
|
||||
return mConditions;
|
||||
}
|
||||
|
||||
public List<Condition> getVisibleConditions() {
|
||||
List<Condition> conditions = new ArrayList<>();
|
||||
final int N = mConditions.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (mConditions.get(i).shouldShow()) {
|
||||
conditions.add(mConditions.get(i));
|
||||
}
|
||||
}
|
||||
Collections.sort(conditions, CONDITION_COMPARATOR);
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public void notifyChanged(Condition condition) {
|
||||
saveToXml();
|
||||
final int N = mListeners.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
mListeners.get(i).onConditionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(ConditionListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
public void remListener(ConditionListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
public static ConditionManager get(Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ConditionManager(context);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public interface ConditionListener {
|
||||
void onConditionsChanged();
|
||||
}
|
||||
|
||||
private static final Comparator<Condition> CONDITION_COMPARATOR = new Comparator<Condition>() {
|
||||
@Override
|
||||
public int compare(Condition lhs, Condition rhs) {
|
||||
return Long.compare(lhs.getLastChange(), rhs.getLastChange());
|
||||
}
|
||||
};
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 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.settings.dashboard.conditional;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* Version of RecyclerView that can have listeners for onWindowFocusChanged.
|
||||
*/
|
||||
public class FocusRecyclerView extends RecyclerView {
|
||||
|
||||
private FocusListener mListener;
|
||||
|
||||
public FocusRecyclerView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
if (mListener != null) {
|
||||
mListener.onWindowFocusChanged(hasWindowFocus);
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener(FocusListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public interface FocusListener {
|
||||
void onWindowFocusChanged(boolean hasWindowFocus);
|
||||
}
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.settings.dashboard.conditional;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.net.wifi.WifiManager;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.TetherSettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settingslib.TetherUtil;
|
||||
|
||||
public class HotspotCondition extends Condition {
|
||||
|
||||
private final WifiManager mWifiManager;
|
||||
|
||||
public HotspotCondition(ConditionManager manager) {
|
||||
super(manager);
|
||||
mWifiManager = mManager.getContext().getSystemService(WifiManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshState() {
|
||||
boolean wifiTetherEnabled = TetherUtil.isWifiTetherEnabled(mManager.getContext());
|
||||
setActive(wifiTetherEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSilenceChanged(boolean silenced) {
|
||||
// Only need to listen for hotspot changes when hotspot has been silenced.
|
||||
PackageManager pm = mManager.getContext().getPackageManager();
|
||||
pm.setComponentEnabledSetting(new ComponentName(mManager.getContext(), Receiver.class),
|
||||
silenced ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return Icon.createWithResource(mManager.getContext(), R.drawable.ic_hotspot);
|
||||
}
|
||||
|
||||
private String getSsid() {
|
||||
WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
|
||||
if (wifiConfig == null) {
|
||||
return mManager.getContext().getString(
|
||||
com.android.internal.R.string.wifi_tether_configure_ssid_default);
|
||||
} else {
|
||||
return wifiConfig.SSID;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mManager.getContext().getString(R.string.condition_hotspot_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mManager.getContext().getString(R.string.condition_hotspot_summary, getSsid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence[] getActions() {
|
||||
return new CharSequence[] { mManager.getContext().getString(R.string.condition_turn_off) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrimaryClick() {
|
||||
Utils.startWithFragment(mManager.getContext(), TetherSettings.class.getName(), null, null,
|
||||
0, R.string.tether_settings_title_all, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionClick(int index) {
|
||||
if (index == 0) {
|
||||
TetherUtil.setWifiTethering(false, mManager.getContext());
|
||||
setActive(false);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected index " + index);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Receiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(intent.getAction())) {
|
||||
ConditionManager.get(context).getCondition(HotspotCondition.class)
|
||||
.refreshState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user