Skip to content

Commit 7f40f24

Browse files
committed
Refactor app restriction fragment
This refactoring is to reduce code duplication when implementing b/28012233. Here are the main things that the refactoring do. 1. Extract the edit dialog to a separate class, so we could reuse it in different key value pair setting. So the caller start the dialog and receives the result in onActivityResult. 2. The dialog handles bundle and bundle array type as well, so no more special handling is needed in caller side. 3. Rewrite StringArrayTypeInputAdapter to avoid passing the adapter around. Also fixed two bugs: 1. Show add button outside of list 2. Fix setting bundle array in app restriction Bug: 28551785 Bug: 23718355 Change-Id: I291514a44879cc8cbf1905bbfb0556c738715bb2
1 parent ef6982a commit 7f40f24

22 files changed

+1641
-1072
lines changed

app/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ android {
9898
}
9999

100100
dependencies {
101-
compile "com.android.support:support-v4:22.0.0"
102-
compile "com.android.support:support-v13:22.0.0"
101+
compile 'com.android.support:appcompat-v7:24.+'
102+
compile 'com.android.support:recyclerview-v7:24.+'
103+
compile "com.android.support:support-v13:24.+"
103104
compile(name:'setup-wizard-lib-platform-release', ext:'aar')
104105
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright (C) 2016 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.afwsamples.testdpc.common;
18+
19+
import android.content.Context;
20+
import android.view.LayoutInflater;
21+
import android.view.View;
22+
import android.view.ViewGroup;
23+
import android.view.ViewParent;
24+
import android.widget.ArrayAdapter;
25+
import android.widget.TextView;
26+
27+
import com.afwsamples.testdpc.R;
28+
29+
import java.util.Collections;
30+
import java.util.Comparator;
31+
import java.util.List;
32+
33+
/**
34+
* List of rows with edit and delete button.
35+
*/
36+
public abstract class EditDeleteArrayAdapter<T> extends ArrayAdapter<T>
37+
implements View.OnClickListener {
38+
39+
private List<T> mEntries;
40+
private OnEditButtonClickListener mOnEditButtonClickListener;
41+
private OnDeleteButtonClickListener mOnDeleteButtonClickListener;
42+
43+
public EditDeleteArrayAdapter(Context context, List<T> entries,
44+
OnEditButtonClickListener onEditButtonClickListener,
45+
OnDeleteButtonClickListener onDeleteButtonClickListener) {
46+
super(context, 0, entries);
47+
mEntries = entries;
48+
mOnEditButtonClickListener = onEditButtonClickListener;
49+
mOnDeleteButtonClickListener = onDeleteButtonClickListener;
50+
}
51+
52+
@Override
53+
public View getView(final int position, View convertView, ViewGroup parent) {
54+
RowViewHolder<T> viewHolder;
55+
if (convertView == null) {
56+
convertView = LayoutInflater.from(getContext()).inflate(
57+
R.layout.edit_delete_row, parent, false);
58+
convertView.findViewById(R.id.edit_row).setOnClickListener(this);
59+
convertView.findViewById(R.id.delete_row).setOnClickListener(this);
60+
61+
viewHolder = new RowViewHolder<>();
62+
convertView.setTag(viewHolder);
63+
viewHolder.restrictionKeyText = (TextView) convertView.findViewById(
64+
R.id.restriction_key);
65+
} else {
66+
viewHolder = (RowViewHolder) convertView.getTag();
67+
}
68+
viewHolder.entry = getItem(position);
69+
viewHolder.restrictionKeyText.setText(getDisplayName(viewHolder.entry));
70+
viewHolder.entryPosition = position;
71+
return convertView;
72+
}
73+
74+
@Override
75+
public void onClick(View view) {
76+
ViewParent parentView = view.getParent();
77+
if (!(parentView instanceof View) || ((View) parentView).getTag() == null) {
78+
return;
79+
}
80+
final RowViewHolder<T> viewHolder =
81+
(RowViewHolder<T>) ((View) parentView).getTag();
82+
final T entry = viewHolder.entry;
83+
if (view.getId() == R.id.edit_row) {
84+
mOnEditButtonClickListener.onEditButtonClick(entry);
85+
} else if (view.getId() == R.id.delete_row) {
86+
remove(entry);
87+
if (mOnDeleteButtonClickListener != null) {
88+
mOnDeleteButtonClickListener.onDeleteButtonClick(entry);
89+
}
90+
}
91+
}
92+
93+
@Override
94+
public void notifyDataSetChanged() {
95+
if (mEntries != null) {
96+
Collections.sort(mEntries, new Comparator<T>() {
97+
@Override
98+
public int compare(T entry1, T entry2) {
99+
return getDisplayName(entry1).compareTo(getDisplayName(entry2));
100+
}
101+
});
102+
}
103+
super.notifyDataSetChanged();
104+
}
105+
106+
public void set(int index, T item) {
107+
mEntries.set(index, item);
108+
notifyDataSetChanged();
109+
}
110+
111+
protected abstract String getDisplayName(T entry);
112+
113+
private static class RowViewHolder<T> {
114+
T entry;
115+
TextView restrictionKeyText;
116+
int entryPosition;
117+
}
118+
119+
public interface OnEditButtonClickListener<T> {
120+
void onEditButtonClick(T entry);
121+
}
122+
123+
public interface OnDeleteButtonClickListener<T> {
124+
void onDeleteButtonClick(T entry);
125+
}
126+
}

app/src/main/java/com/afwsamples/testdpc/common/ManageAppFragment.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import android.widget.AdapterView;
2727
import android.widget.ListView;
2828
import android.widget.Spinner;
29+
import android.widget.TextView;
2930

3031
import com.afwsamples.testdpc.R;
3132

@@ -37,10 +38,11 @@
3738
* This fragment shows a spinner of all allowed apps and a list of properties associated with the
3839
* currently selected application.
3940
*/
40-
public abstract class ManageAppFragment extends Fragment {
41+
public abstract class ManageAppFragment extends Fragment implements View.OnClickListener {
4142

4243
protected PackageManager mPackageManager;
4344
protected Spinner mManagedAppsSpinner;
45+
protected TextView mHeaderView;
4446
protected ListView mAppListView;
4547

4648
@Override
@@ -65,6 +67,7 @@ public View onCreateView(LayoutInflater layoutInflater, ViewGroup container,
6567
new ApplicationInfo.DisplayNameComparator(mPackageManager));
6668
AppInfoSpinnerAdapter appInfoSpinnerAdapter = new AppInfoSpinnerAdapter(getActivity(),
6769
R.layout.app_row, R.id.pkg_name, managedAppList);
70+
mHeaderView = (TextView) view.findViewById(R.id.header_text);
6871
mManagedAppsSpinner = (Spinner) view.findViewById(R.id.managed_apps_list);
6972
mManagedAppsSpinner.setAdapter(appInfoSpinnerAdapter);
7073
mManagedAppsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@@ -79,6 +82,10 @@ public void onNothingSelected(AdapterView<?> parent) {
7982
}
8083
});
8184
mAppListView = (ListView) view.findViewById(R.id.app_list_view);
85+
view.findViewById(R.id.save_app).setOnClickListener(this);
86+
view.findViewById(R.id.reset_app).setOnClickListener(this);
87+
view.findViewById(R.id.add_new_row).setOnClickListener(this);
88+
view.findViewById(R.id.load_default_button).setOnClickListener(this);
8289
loadData(((ApplicationInfo) mManagedAppsSpinner.getSelectedItem()).packageName);
8390
return view;
8491
}
@@ -101,4 +108,25 @@ private List<ApplicationInfo> getInstalledLaunchableApps() {
101108
*/
102109
protected abstract void loadData(String pkgName);
103110

111+
@Override
112+
public void onClick(View view) {
113+
switch (view.getId()) {
114+
case R.id.reset_app:
115+
resetConfig();
116+
break;
117+
case R.id.save_app:
118+
saveConfig();
119+
break;
120+
case R.id.add_new_row:
121+
addNewRow();
122+
break;
123+
case R.id.load_default_button:
124+
loadDefault();
125+
}
126+
}
127+
128+
protected abstract void resetConfig();
129+
protected abstract void saveConfig();
130+
protected abstract void addNewRow();
131+
protected abstract void loadDefault();
104132
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (C) 2016 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.afwsamples.testdpc.common;
18+
19+
import android.support.v7.widget.RecyclerView;
20+
import android.text.Editable;
21+
import android.text.TextWatcher;
22+
import android.view.LayoutInflater;
23+
import android.view.View;
24+
import android.view.ViewGroup;
25+
import android.widget.EditText;
26+
import android.widget.ImageView;
27+
28+
import com.afwsamples.testdpc.R;
29+
30+
import java.util.ArrayList;
31+
import java.util.List;
32+
33+
public class StringArrayTypeInputAdapter extends
34+
RecyclerView.Adapter<StringArrayTypeInputAdapter.ViewHolder> {
35+
private List<String> mStringList;
36+
37+
public StringArrayTypeInputAdapter() {
38+
mStringList = new ArrayList<>();
39+
}
40+
41+
@Override
42+
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
43+
View view = LayoutInflater.from(parent.getContext())
44+
.inflate(R.layout.string_array_row, parent, false);
45+
return new ViewHolder(view);
46+
}
47+
48+
@Override
49+
public void onBindViewHolder(final ViewHolder holder, final int position) {
50+
holder.stringValue.setText(mStringList.get(position));
51+
if (holder.textWatcher != null) {
52+
holder.stringValue.removeTextChangedListener(holder.textWatcher);
53+
}
54+
holder.textWatcher = createEditTextTextWatcher(holder);
55+
holder.stringValue.addTextChangedListener(holder.textWatcher);
56+
holder.delete.setOnClickListener(new View.OnClickListener() {
57+
@Override
58+
public void onClick(View view) {
59+
int adapterPosition = holder.getAdapterPosition();
60+
// make sure the view is not removed yet.
61+
if (adapterPosition != -1) {
62+
mStringList.remove(adapterPosition);
63+
notifyItemRemoved(adapterPosition);
64+
}
65+
}
66+
});
67+
}
68+
69+
@Override
70+
public int getItemCount() {
71+
return mStringList.size();
72+
}
73+
74+
public List<String> getStringList() {
75+
return mStringList;
76+
}
77+
78+
public void setStringList(List<String> stringList) {
79+
mStringList = stringList;
80+
notifyDataSetChanged();
81+
}
82+
83+
public class ViewHolder extends RecyclerView.ViewHolder {
84+
public EditText stringValue;
85+
public ImageView delete;
86+
public TextWatcher textWatcher;
87+
88+
public ViewHolder(View view) {
89+
super(view);
90+
stringValue = (EditText) view.findViewById(R.id.string_input);
91+
delete = (ImageView) view.findViewById(R.id.delete_row);
92+
}
93+
}
94+
95+
private TextWatcher createEditTextTextWatcher(final ViewHolder viewHolder) {
96+
return new TextWatcher() {
97+
@Override
98+
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
99+
100+
@Override
101+
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
102+
103+
@Override
104+
public void afterTextChanged(Editable editable) {
105+
mStringList.set(viewHolder.getAdapterPosition(), editable.toString());
106+
}
107+
};
108+
}
109+
}

0 commit comments

Comments
 (0)