Add conditionals to Settings
Also add Airplane Mode and Hotspot conditionals (more to come soon) Change-Id: I11f206db59f7c715f416fb5852b8f0fcb857a247
This commit is contained in:
@@ -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