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