Revision 74a77186
res/layout/activity_app_select.xml | ||
---|---|---|
1 |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
xmlns:tools="http://schemas.android.com/tools" |
|
3 |
android:layout_width="match_parent" |
|
4 |
android:layout_height="match_parent" > |
|
5 |
|
|
6 |
<fragment |
|
7 |
android:id="@+id/fragment_app_list" |
|
8 |
android:layout_width="fill_parent" |
|
9 |
android:layout_height="fill_parent" |
|
10 |
class="info.guardianproject.checkey.AppListFragment" /> |
|
11 |
|
|
12 |
</RelativeLayout> |
res/layout/app_entry.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8"?> |
|
2 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
3 |
android:layout_width="fill_parent" |
|
4 |
android:layout_height="wrap_content" |
|
5 |
android:orientation="horizontal" > |
|
6 |
|
|
7 |
<ImageView |
|
8 |
android:id="@+id/icon" |
|
9 |
android:layout_width="48dip" |
|
10 |
android:layout_height="48dip" |
|
11 |
android:contentDescription="@string/icon" /> |
|
12 |
|
|
13 |
<TextView |
|
14 |
android:id="@+id/text" |
|
15 |
android:layout_width="fill_parent" |
|
16 |
android:layout_height="wrap_content" |
|
17 |
android:gravity="left|center_vertical" |
|
18 |
android:textSize="24sp" /> |
|
19 |
|
|
20 |
</LinearLayout> |
res/layout/fragment_main.xml | ||
---|---|---|
1 |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
2 |
xmlns:tools="http://schemas.android.com/tools" |
|
3 |
android:layout_width="match_parent" |
|
4 |
android:layout_height="match_parent" |
|
5 |
android:paddingBottom="@dimen/activity_vertical_margin" |
|
6 |
android:paddingLeft="@dimen/activity_horizontal_margin" |
|
7 |
android:paddingRight="@dimen/activity_horizontal_margin" |
|
8 |
android:paddingTop="@dimen/activity_vertical_margin" |
|
9 |
tools:context="info.guardianproject.checkey.MainActivity$PlaceholderFragment" > |
|
10 |
|
|
11 |
<TextView |
|
12 |
android:layout_width="wrap_content" |
|
13 |
android:layout_height="wrap_content" |
|
14 |
android:text="@string/hello_world" /> |
|
15 |
|
|
16 |
</RelativeLayout> |
res/layout/select_local_apps_list_item.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8"?> |
|
2 |
<!-- |
|
3 |
Copyright (C) 2010 The Android Open Source Project |
|
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 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
--> |
|
14 |
|
|
15 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
16 |
xmlns:tools="http://schemas.android.com/tools" |
|
17 |
android:layout_width="match_parent" |
|
18 |
android:layout_height="wrap_content" |
|
19 |
android:background="?android:attr/activatedBackgroundIndicator" |
|
20 |
android:minHeight="?android:attr/listPreferredItemHeight" |
|
21 |
android:paddingBottom="2dip" |
|
22 |
android:paddingTop="2dip" |
|
23 |
tools:ignore="NewApi" > |
|
24 |
|
|
25 |
<!-- TODO remove NewApi ignore when appcompat-v7 is in place --> |
|
26 |
|
|
27 |
<ImageView |
|
28 |
android:id="@+id/icon" |
|
29 |
android:layout_width="48dip" |
|
30 |
android:layout_height="48dip" |
|
31 |
android:layout_marginLeft="?android:attr/listPreferredItemPaddingLeft" |
|
32 |
android:layout_marginStart="?android:attr/listPreferredItemPaddingLeft" |
|
33 |
android:layout_marginTop="6dip" |
|
34 |
tools:ignore="ContentDescription" /> |
|
35 |
|
|
36 |
<TwoLineListItem |
|
37 |
android:layout_width="match_parent" |
|
38 |
android:layout_height="wrap_content" |
|
39 |
android:mode="twoLine" > |
|
40 |
|
|
41 |
<TextView |
|
42 |
android:id="@+id/application_label" |
|
43 |
android:layout_width="match_parent" |
|
44 |
android:layout_height="wrap_content" |
|
45 |
android:layout_marginLeft="?android:attr/listPreferredItemPaddingLeft" |
|
46 |
android:layout_marginStart="?android:attr/listPreferredItemPaddingLeft" |
|
47 |
android:layout_marginTop="6dip" |
|
48 |
android:textAppearance="?android:attr/textAppearanceListItem" /> |
|
49 |
|
|
50 |
<TextView |
|
51 |
android:id="@+id/package_name" |
|
52 |
android:layout_width="match_parent" |
|
53 |
android:layout_height="wrap_content" |
|
54 |
android:layout_alignStart="@+id/application_label" |
|
55 |
android:layout_below="@+id/application_label" |
|
56 |
android:textAppearance="?android:attr/textAppearanceSmall" /> |
|
57 |
</TwoLineListItem> |
|
58 |
|
|
59 |
</LinearLayout> |
res/menu/main.xml | ||
---|---|---|
5 | 5 |
|
6 | 6 |
<item |
7 | 7 |
android:id="@+id/action_settings" |
8 |
android:icon="@android:drawable/ic_menu_preferences" |
|
8 | 9 |
android:orderInCategory="100" |
9 | 10 |
android:title="@string/action_settings" |
10 | 11 |
app:showAsAction="never"/> |
11 | 12 |
|
12 |
</menu> |
|
13 |
</menu> |
res/values/strings.xml | ||
---|---|---|
2 | 2 |
<resources> |
3 | 3 |
|
4 | 4 |
<string name="app_name">ChecKey</string> |
5 |
<string name="hello_world">Hello world!</string>
|
|
5 |
<string name="icon">icon</string>
|
|
6 | 6 |
<string name="action_settings">Settings</string> |
7 |
<string name="no_applications_found">No applications found</string> |
|
7 | 8 |
|
8 |
</resources> |
|
9 |
</resources> |
src/info/guardianproject/checkey/AppEntry.java | ||
---|---|---|
1 |
/* |
|
2 |
Licensed under the Apache License, Version 2.0 (the "License"); |
|
3 |
you may not use this file except in compliance with the License. |
|
4 |
You may obtain a copy of the License at |
|
5 |
|
|
6 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
|
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
|
|
14 |
Based on Paul Blundell's Tutorial: |
|
15 |
http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ |
|
16 |
|
|
17 |
which is originally based on: |
|
18 |
https://developer.android.com/reference/android/content/AsyncTaskLoader.html |
|
19 |
*/ |
|
20 |
|
|
21 |
package info.guardianproject.checkey; |
|
22 |
|
|
23 |
import android.content.pm.ApplicationInfo; |
|
24 |
import android.content.pm.PackageManager; |
|
25 |
import android.graphics.drawable.Drawable; |
|
26 |
|
|
27 |
import java.io.File; |
|
28 |
|
|
29 |
public class AppEntry { |
|
30 |
public static final String TAG = "AppEntry"; |
|
31 |
|
|
32 |
private final AppListLoader loader; |
|
33 |
private final ApplicationInfo info; |
|
34 |
private final File apkFile; |
|
35 |
private boolean enabled; |
|
36 |
private String label; |
|
37 |
private Drawable icon; |
|
38 |
private boolean mounted; |
|
39 |
|
|
40 |
public AppEntry(AppListLoader loader, ApplicationInfo info) { |
|
41 |
this.loader = loader; |
|
42 |
this.info = info; |
|
43 |
apkFile = new File(info.sourceDir); |
|
44 |
} |
|
45 |
|
|
46 |
public ApplicationInfo getApplicationInfo() { |
|
47 |
return info; |
|
48 |
} |
|
49 |
|
|
50 |
public String getPackageName() { |
|
51 |
return info.packageName; |
|
52 |
} |
|
53 |
|
|
54 |
public String getLabel() { |
|
55 |
return label; |
|
56 |
} |
|
57 |
|
|
58 |
public boolean isEnabled() { |
|
59 |
return enabled; |
|
60 |
} |
|
61 |
|
|
62 |
public void setEnabled(boolean enabled) { |
|
63 |
this.enabled = enabled; |
|
64 |
} |
|
65 |
|
|
66 |
public Drawable getIcon() { |
|
67 |
if (icon == null) { |
|
68 |
if (apkFile.exists()) { |
|
69 |
icon = info.loadIcon(loader.pm); |
|
70 |
return icon; |
|
71 |
} else { |
|
72 |
mounted = false; |
|
73 |
} |
|
74 |
} else if (!mounted) { |
|
75 |
// If the app wasn't mounted but is now mounted, reload its icon |
|
76 |
if (apkFile.exists()) { |
|
77 |
mounted = true; |
|
78 |
icon = info.loadIcon(loader.pm); |
|
79 |
} |
|
80 |
} else { |
|
81 |
return icon; |
|
82 |
} |
|
83 |
|
|
84 |
return loader.getContext().getResources().getDrawable(android.R.drawable.sym_def_app_icon); |
|
85 |
} |
|
86 |
|
|
87 |
@Override |
|
88 |
public String toString() { |
|
89 |
return label; |
|
90 |
} |
|
91 |
|
|
92 |
public void loadLabel(PackageManager pm) { |
|
93 |
if (label == null || !mounted) { |
|
94 |
if (apkFile.exists()) { |
|
95 |
mounted = true; |
|
96 |
CharSequence label = null; |
|
97 |
label = info.loadLabel(pm); |
|
98 |
this.label = label != null ? label.toString() : info.packageName; |
|
99 |
} else { |
|
100 |
mounted = false; |
|
101 |
label = info.packageName; |
|
102 |
} |
|
103 |
} |
|
104 |
} |
|
105 |
} |
src/info/guardianproject/checkey/AppListAdapter.java | ||
---|---|---|
1 |
/* |
|
2 |
Licensed under the Apache License, Version 2.0 (the "License"); |
|
3 |
you may not use this file except in compliance with the License. |
|
4 |
You may obtain a copy of the License at |
|
5 |
|
|
6 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
|
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
|
|
14 |
Based on Paul Blundell's Tutorial: |
|
15 |
http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ |
|
16 |
|
|
17 |
which is originally based on: |
|
18 |
https://developer.android.com/reference/android/content/AsyncTaskLoader.html |
|
19 |
*/ |
|
20 |
|
|
21 |
package info.guardianproject.checkey; |
|
22 |
|
|
23 |
import android.content.Context; |
|
24 |
import android.util.SparseArray; |
|
25 |
import android.view.LayoutInflater; |
|
26 |
import android.view.View; |
|
27 |
import android.view.ViewGroup; |
|
28 |
import android.widget.BaseAdapter; |
|
29 |
import android.widget.ImageView; |
|
30 |
import android.widget.TextView; |
|
31 |
|
|
32 |
import java.util.List; |
|
33 |
|
|
34 |
public class AppListAdapter extends BaseAdapter { |
|
35 |
|
|
36 |
private final LayoutInflater inflater; |
|
37 |
private List<AppEntry> data; |
|
38 |
|
|
39 |
public AppListAdapter(Context context) { |
|
40 |
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
|
41 |
} |
|
42 |
|
|
43 |
public void setData(List<AppEntry> data) { |
|
44 |
this.data = data; |
|
45 |
notifyDataSetChanged(); |
|
46 |
} |
|
47 |
|
|
48 |
@Override |
|
49 |
public View getView(int position, View view, ViewGroup parent) { |
|
50 |
if (view == null) |
|
51 |
view = inflater.inflate(R.layout.select_local_apps_list_item, parent, false); |
|
52 |
|
|
53 |
AppEntry appEntry = data.get(position); |
|
54 |
ImageView iconView = ViewHolder.get(view, R.id.icon); |
|
55 |
iconView.setImageDrawable(appEntry.getIcon()); |
|
56 |
TextView labelView = ViewHolder.get(view, R.id.application_label); |
|
57 |
labelView.setText(appEntry.getLabel()); |
|
58 |
TextView packageView = ViewHolder.get(view, R.id.package_name); |
|
59 |
packageView.setText(appEntry.getPackageName()); |
|
60 |
return view; |
|
61 |
} |
|
62 |
|
|
63 |
@Override |
|
64 |
public int getCount() { |
|
65 |
return data == null ? 0 : data.size(); |
|
66 |
} |
|
67 |
|
|
68 |
@Override |
|
69 |
public Object getItem(int position) { |
|
70 |
return data == null ? null : data.get(position); |
|
71 |
} |
|
72 |
|
|
73 |
@Override |
|
74 |
public long getItemId(int position) { |
|
75 |
return position; |
|
76 |
} |
|
77 |
|
|
78 |
/* from http://www.piwai.info/android-adapter-good-practices/#Update */ |
|
79 |
public static class ViewHolder { |
|
80 |
// a generic return type to reduce the casting noise in client code |
|
81 |
@SuppressWarnings("unchecked") |
|
82 |
public static <T extends View> T get(View view, int id) { |
|
83 |
SparseArray<View> viewHolder = (SparseArray<View>) view.getTag(); |
|
84 |
if (viewHolder == null) { |
|
85 |
viewHolder = new SparseArray<View>(); |
|
86 |
view.setTag(viewHolder); |
|
87 |
} |
|
88 |
View childView = viewHolder.get(id); |
|
89 |
if (childView == null) { |
|
90 |
childView = view.findViewById(id); |
|
91 |
viewHolder.put(id, childView); |
|
92 |
} |
|
93 |
return (T) childView; |
|
94 |
} |
|
95 |
} |
|
96 |
|
|
97 |
} |
src/info/guardianproject/checkey/AppListFragment.java | ||
---|---|---|
1 |
/* |
|
2 |
Licensed under the Apache License, Version 2.0 (the "License"); |
|
3 |
you may not use this file except in compliance with the License. |
|
4 |
You may obtain a copy of the License at |
|
5 |
|
|
6 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
|
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
|
|
14 |
Based on Paul Blundell's Tutorial: |
|
15 |
http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ |
|
16 |
|
|
17 |
which is originally based on: |
|
18 |
https://developer.android.com/reference/android/content/AsyncTaskLoader.html |
|
19 |
*/ |
|
20 |
|
|
21 |
package info.guardianproject.checkey; |
|
22 |
|
|
23 |
import android.annotation.TargetApi; |
|
24 |
import android.os.Bundle; |
|
25 |
import android.support.v4.app.ListFragment; |
|
26 |
import android.support.v4.app.LoaderManager.LoaderCallbacks; |
|
27 |
import android.support.v4.content.Loader; |
|
28 |
import android.view.View; |
|
29 |
import android.widget.ListView; |
|
30 |
|
|
31 |
import java.util.List; |
|
32 |
|
|
33 |
public class AppListFragment extends ListFragment implements LoaderCallbacks<List<AppEntry>> { |
|
34 |
|
|
35 |
private AppListAdapter adapter; |
|
36 |
|
|
37 |
@Override |
|
38 |
public void onActivityCreated(Bundle savedInstanceState) { |
|
39 |
super.onActivityCreated(savedInstanceState); |
|
40 |
|
|
41 |
setEmptyText(getString(R.string.no_applications_found)); |
|
42 |
|
|
43 |
adapter = new AppListAdapter(getActivity()); |
|
44 |
setListAdapter(adapter); |
|
45 |
setListShown(false); |
|
46 |
|
|
47 |
// Prepare the loader |
|
48 |
// either reconnect with an existing one or start a new one |
|
49 |
getLoaderManager().initLoader(0, null, this); |
|
50 |
} |
|
51 |
|
|
52 |
@TargetApi(11) |
|
53 |
// TODO replace with appcompat-v7 |
|
54 |
@Override |
|
55 |
public void onListItemClick(ListView l, View v, int position, long id) { |
|
56 |
AppEntry appEntry = (AppEntry) adapter.getItem(position); |
|
57 |
// TODO search androidobs here |
|
58 |
} |
|
59 |
|
|
60 |
@Override |
|
61 |
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) { |
|
62 |
// This is called when a new loader needs to be created. |
|
63 |
// This sample only has one Loader with no arguments, so it is simple. |
|
64 |
return new AppListLoader(getActivity()); |
|
65 |
} |
|
66 |
|
|
67 |
@Override |
|
68 |
public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) { |
|
69 |
adapter.setData(data); |
|
70 |
|
|
71 |
// The list should now be shown |
|
72 |
if (isResumed()) { |
|
73 |
setListShown(true); |
|
74 |
} else { |
|
75 |
setListShownNoAnimation(true); |
|
76 |
} |
|
77 |
} |
|
78 |
|
|
79 |
@Override |
|
80 |
public void onLoaderReset(Loader<List<AppEntry>> loader) { |
|
81 |
// Clear the data in the adapter |
|
82 |
adapter.setData(null); |
|
83 |
} |
|
84 |
} |
src/info/guardianproject/checkey/AppListLoader.java | ||
---|---|---|
1 |
/* |
|
2 |
Licensed under the Apache License, Version 2.0 (the "License"); |
|
3 |
you may not use this file except in compliance with the License. |
|
4 |
You may obtain a copy of the License at |
|
5 |
|
|
6 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
|
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
|
|
14 |
Based on Paul Blundell's Tutorial: |
|
15 |
http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ |
|
16 |
|
|
17 |
which is originally based on: |
|
18 |
https://developer.android.com/reference/android/content/AsyncTaskLoader.html |
|
19 |
*/ |
|
20 |
|
|
21 |
package info.guardianproject.checkey; |
|
22 |
|
|
23 |
import android.content.Context; |
|
24 |
import android.content.pm.ApplicationInfo; |
|
25 |
import android.content.pm.PackageManager; |
|
26 |
import android.support.v4.content.AsyncTaskLoader; |
|
27 |
|
|
28 |
import java.text.Collator; |
|
29 |
import java.util.ArrayList; |
|
30 |
import java.util.Collections; |
|
31 |
import java.util.List; |
|
32 |
|
|
33 |
public class AppListLoader extends AsyncTaskLoader<List<AppEntry>> { |
|
34 |
public final PackageManager pm; |
|
35 |
|
|
36 |
List<AppEntry> apps; |
|
37 |
PackageIntentReceiver packageObserver; |
|
38 |
|
|
39 |
public AppListLoader(Context context) { |
|
40 |
super(context); |
|
41 |
|
|
42 |
// Retrieve the package manager for later use; note we don't |
|
43 |
// use 'context' directly but instead the safe global application |
|
44 |
// context returned by getContext(). |
|
45 |
pm = getContext().getPackageManager(); |
|
46 |
} |
|
47 |
|
|
48 |
@Override |
|
49 |
protected void onStartLoading() { |
|
50 |
super.onStartLoading(); |
|
51 |
// AsyncTaskLoader doesnt start unless you forceLoad |
|
52 |
// http://code.google.com/p/android/issues/detail?id=14944 |
|
53 |
if (apps != null) { |
|
54 |
deliverResult(apps); |
|
55 |
} |
|
56 |
if (takeContentChanged() || apps == null) { |
|
57 |
forceLoad(); |
|
58 |
} |
|
59 |
} |
|
60 |
|
|
61 |
/** |
|
62 |
* This is where the bulk of the work. This function is called in a |
|
63 |
* background thread and should generate a new set of data to be published |
|
64 |
* by the loader. |
|
65 |
*/ |
|
66 |
@Override |
|
67 |
public List<AppEntry> loadInBackground() { |
|
68 |
// Retrieve all known applications |
|
69 |
List<ApplicationInfo> apps = pm.getInstalledApplications( |
|
70 |
PackageManager.GET_UNINSTALLED_PACKAGES | |
|
71 |
PackageManager.GET_DISABLED_COMPONENTS); |
|
72 |
|
|
73 |
if (apps == null) { |
|
74 |
apps = new ArrayList<ApplicationInfo>(); |
|
75 |
} |
|
76 |
|
|
77 |
// Create corresponding array of entries and load their labels |
|
78 |
List<AppEntry> entries = new ArrayList<AppEntry>(apps.size()); |
|
79 |
for (ApplicationInfo applicationInfo : apps) { |
|
80 |
AppEntry entry = new AppEntry(this, applicationInfo); |
|
81 |
entry.loadLabel(pm); |
|
82 |
entries.add(entry); |
|
83 |
} |
|
84 |
|
|
85 |
Collections.sort(entries, Comparator.ALPHA_COMPARATOR); |
|
86 |
|
|
87 |
return entries; |
|
88 |
} |
|
89 |
|
|
90 |
/** |
|
91 |
* Called when there is new data to deliver to the client. The super class |
|
92 |
* will take care of delivering it; the implementation just adds a little |
|
93 |
* more logic |
|
94 |
*/ |
|
95 |
@Override |
|
96 |
public void deliverResult(List<AppEntry> apps) { |
|
97 |
if (isReset()) { |
|
98 |
// An async query came in while the loader is stopped. We don't need |
|
99 |
// the result |
|
100 |
if (apps != null) { |
|
101 |
onReleaseResources(apps); |
|
102 |
} |
|
103 |
} |
|
104 |
List<AppEntry> oldApps = this.apps; |
|
105 |
this.apps = apps; |
|
106 |
|
|
107 |
if (isStarted()) { |
|
108 |
// If the loader is currently started, we can immediately deliver a |
|
109 |
// result |
|
110 |
super.deliverResult(apps); |
|
111 |
} |
|
112 |
|
|
113 |
// At this point we can release the resources associated with 'oldApps' |
|
114 |
// if needed; |
|
115 |
// now that the new result is delivered we know that it is no longer in |
|
116 |
// use |
|
117 |
if (oldApps != null) { |
|
118 |
onReleaseResources(oldApps); |
|
119 |
} |
|
120 |
} |
|
121 |
|
|
122 |
@Override |
|
123 |
protected void onStopLoading() { |
|
124 |
// Attempts to cancel the current load task if possible |
|
125 |
cancelLoad(); |
|
126 |
} |
|
127 |
|
|
128 |
@Override |
|
129 |
public void onCanceled(List<AppEntry> apps) { |
|
130 |
super.onCanceled(apps); |
|
131 |
|
|
132 |
// At this point we can release the resources associated with 'apps' if |
|
133 |
// needed |
|
134 |
onReleaseResources(apps); |
|
135 |
} |
|
136 |
|
|
137 |
/** |
|
138 |
* Handles request to completely reset the loader |
|
139 |
*/ |
|
140 |
@Override |
|
141 |
protected void onReset() { |
|
142 |
super.onReset(); |
|
143 |
|
|
144 |
// ensure the loader is stopped |
|
145 |
onStopLoading(); |
|
146 |
|
|
147 |
// At this point we can release the resources associated with 'apps' if |
|
148 |
// needed |
|
149 |
if (apps != null) { |
|
150 |
onReleaseResources(apps); |
|
151 |
apps = null; |
|
152 |
} |
|
153 |
|
|
154 |
// Stop monitoring for changes |
|
155 |
if (packageObserver != null) { |
|
156 |
getContext().unregisterReceiver(packageObserver); |
|
157 |
packageObserver = null; |
|
158 |
} |
|
159 |
} |
|
160 |
|
|
161 |
/** |
|
162 |
* Helper function to take care of releasing resources associated with an |
|
163 |
* actively loaded data set |
|
164 |
*/ |
|
165 |
private void onReleaseResources(List<AppEntry> apps) { |
|
166 |
// For a simple list there is nothing to do |
|
167 |
// but for a Cursor we would close it here |
|
168 |
} |
|
169 |
|
|
170 |
/** |
|
171 |
* Perform alphabetical comparison on AppEntry objects |
|
172 |
*/ |
|
173 |
static class Comparator { |
|
174 |
static final java.util.Comparator<AppEntry> ALPHA_COMPARATOR = new java.util.Comparator<AppEntry>() { |
|
175 |
private final Collator collator = Collator.getInstance(); |
|
176 |
|
|
177 |
@Override |
|
178 |
public int compare(AppEntry lhs, AppEntry rhs) { |
|
179 |
return collator.compare(lhs.getLabel(), rhs.getLabel()); |
|
180 |
} |
|
181 |
}; |
|
182 |
} |
|
183 |
} |
src/info/guardianproject/checkey/MainActivity.java | ||
---|---|---|
1 | 1 |
|
2 | 2 |
package info.guardianproject.checkey; |
3 | 3 |
|
4 |
import android.support.v7.app.ActionBarActivity; |
|
5 |
import android.support.v7.app.ActionBar; |
|
6 |
import android.support.v4.app.Fragment; |
|
4 |
import android.annotation.TargetApi; |
|
7 | 5 |
import android.os.Bundle; |
8 |
import android.view.LayoutInflater;
|
|
6 |
import android.support.v4.app.FragmentActivity;
|
|
9 | 7 |
import android.view.Menu; |
10 | 8 |
import android.view.MenuItem; |
11 |
import android.view.View; |
|
12 |
import android.view.ViewGroup; |
|
13 |
import android.os.Build; |
|
14 | 9 |
|
15 |
public class MainActivity extends ActionBarActivity { |
|
10 |
@TargetApi(11) |
|
11 |
// TODO replace with appcompat-v7 |
|
12 |
public class MainActivity extends FragmentActivity { |
|
13 |
private final String TAG = "MainActivity"; |
|
14 |
private AppListFragment appListFragment = null; |
|
16 | 15 |
|
17 | 16 |
@Override |
18 | 17 |
protected void onCreate(Bundle savedInstanceState) { |
19 | 18 |
super.onCreate(savedInstanceState); |
20 |
setContentView(R.layout.activity_main); |
|
19 |
setContentView(R.layout.activity_app_select); |
|
20 |
} |
|
21 | 21 |
|
22 |
if (savedInstanceState == null) { |
|
23 |
getSupportFragmentManager().beginTransaction() |
|
24 |
.add(R.id.container, new PlaceholderFragment()) |
|
25 |
.commit(); |
|
26 |
} |
|
22 |
@Override |
|
23 |
protected void onResume() { |
|
24 |
super.onResume(); |
|
25 |
if (appListFragment == null) |
|
26 |
appListFragment = (AppListFragment) getSupportFragmentManager().findFragmentById( |
|
27 |
R.id.fragment_app_list); |
|
27 | 28 |
} |
28 | 29 |
|
29 | 30 |
@Override |
30 | 31 |
public boolean onCreateOptionsMenu(Menu menu) { |
31 |
|
|
32 |
// Inflate the menu; this adds items to the action bar if it is present. |
|
33 | 32 |
getMenuInflater().inflate(R.menu.main, menu); |
34 | 33 |
return true; |
35 | 34 |
} |
36 | 35 |
|
37 | 36 |
@Override |
38 | 37 |
public boolean onOptionsItemSelected(MenuItem item) { |
39 |
// Handle action bar item clicks here. The action bar will |
|
40 |
// automatically handle clicks on the Home/Up button, so long |
|
41 |
// as you specify a parent activity in AndroidManifest.xml. |
|
42 |
int id = item.getItemId(); |
|
43 |
if (id == R.id.action_settings) { |
|
44 |
return true; |
|
38 |
switch (item.getItemId()) { |
|
39 |
case android.R.id.home: |
|
40 |
setResult(RESULT_CANCELED); |
|
41 |
finish(); |
|
42 |
return true; |
|
43 |
case R.id.action_settings: |
|
44 |
return true; |
|
45 | 45 |
} |
46 | 46 |
return super.onOptionsItemSelected(item); |
47 | 47 |
} |
48 |
|
|
49 |
/** |
|
50 |
* A placeholder fragment containing a simple view. |
|
51 |
*/ |
|
52 |
public static class PlaceholderFragment extends Fragment { |
|
53 |
|
|
54 |
public PlaceholderFragment() { |
|
55 |
} |
|
56 |
|
|
57 |
@Override |
|
58 |
public View onCreateView(LayoutInflater inflater, ViewGroup container, |
|
59 |
Bundle savedInstanceState) { |
|
60 |
View rootView = inflater.inflate(R.layout.fragment_main, container, false); |
|
61 |
return rootView; |
|
62 |
} |
|
63 |
} |
|
64 |
|
|
65 | 48 |
} |
src/info/guardianproject/checkey/PackageIntentReceiver.java | ||
---|---|---|
1 |
/* |
|
2 |
Licensed under the Apache License, Version 2.0 (the "License"); |
|
3 |
you may not use this file except in compliance with the License. |
|
4 |
You may obtain a copy of the License at |
|
5 |
|
|
6 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
|
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
|
|
14 |
Based on Paul Blundell's Tutorial: |
|
15 |
http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ |
|
16 |
|
|
17 |
which is originally based on: |
|
18 |
https://developer.android.com/reference/android/content/AsyncTaskLoader.html |
|
19 |
*/ |
|
20 |
|
|
21 |
package info.guardianproject.checkey; |
|
22 |
|
|
23 |
import android.content.BroadcastReceiver; |
|
24 |
import android.content.Context; |
|
25 |
import android.content.Intent; |
|
26 |
import android.content.IntentFilter; |
|
27 |
|
|
28 |
/** |
|
29 |
* Looks for changes to the installed apps so the loader can be updated |
|
30 |
*/ |
|
31 |
public class PackageIntentReceiver extends BroadcastReceiver { |
|
32 |
final AppListLoader loader; |
|
33 |
|
|
34 |
public PackageIntentReceiver(AppListLoader loader) { |
|
35 |
this.loader = loader; |
|
36 |
IntentFilter filter = new IntentFilter(); |
|
37 |
filter.addAction(Intent.ACTION_PACKAGE_ADDED); |
|
38 |
filter.addAction(Intent.ACTION_PACKAGE_REMOVED); |
|
39 |
filter.addAction(Intent.ACTION_PACKAGE_CHANGED); |
|
40 |
filter.addDataScheme("package"); |
|
41 |
loader.getContext().registerReceiver(this, filter); |
|
42 |
// Register for events related to SD Card installation |
|
43 |
IntentFilter sdFilter = new IntentFilter(); |
|
44 |
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); |
|
45 |
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); |
|
46 |
loader.getContext().registerReceiver(this, sdFilter); |
|
47 |
} |
|
48 |
|
|
49 |
@Override |
|
50 |
public void onReceive(Context context, Intent intent) { |
|
51 |
loader.onContentChanged(); |
|
52 |
} |
|
53 |
|
|
54 |
} |
Also available in: Unified diff