Add conditionals to Settings

Also add Airplane Mode and Hotspot conditionals (more to come soon)

Change-Id: I11f206db59f7c715f416fb5852b8f0fcb857a247
This commit is contained in:
Jason Monk
2015-12-11 16:48:31 -05:00
parent a48f16fe01
commit db4ed191de
18 changed files with 1059 additions and 40 deletions

View File

@@ -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);

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}
}

View 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);
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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());
}
};
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}
}