From 47d4526f05bd5485f717e25a2de3f40cfbc6d05a Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 16:39:36 -0400 Subject: [PATCH 01/14] include new nanohttpd setup in Eclipse project --- .classpath | 1 + 1 file changed, 1 insertion(+) diff --git a/.classpath b/.classpath index d2ce7e889..fd733198e 100644 --- a/.classpath +++ b/.classpath @@ -4,6 +4,7 @@ + From 3c005006ce2f9f608430bd5860729ab4799c5ecd Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 17:07:45 -0400 Subject: [PATCH 02/14] zxing requires >= android-8, only gen QR Codes on >= android-8 The zxing library includes some Java 7 features, so it will only work on Android SDK 8 (2.1) or above. FDroid supports SDK 7 (2.0), so on 7, do not try to generate QR Codes. --- src/org/fdroid/fdroid/QrGenAsyncTask.java | 2 ++ src/org/fdroid/fdroid/views/LocalRepoActivity.java | 3 ++- .../fdroid/fdroid/views/QrWizardDownloadActivity.java | 10 ++++------ .../fdroid/views/QrWizardWifiNetworkActivity.java | 11 ++++------- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/org/fdroid/fdroid/QrGenAsyncTask.java b/src/org/fdroid/fdroid/QrGenAsyncTask.java index ef2f62787..ebfc0fc19 100644 --- a/src/org/fdroid/fdroid/QrGenAsyncTask.java +++ b/src/org/fdroid/fdroid/QrGenAsyncTask.java @@ -16,6 +16,8 @@ import com.google.zxing.WriterException; import com.google.zxing.encode.Contents; import com.google.zxing.encode.QRCodeEncoder; +// zxing is android-8 and above +@TargetApi(8) public class QrGenAsyncTask extends AsyncTask { private static final String TAG = "QrGenAsyncTask"; diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index 81b82fb2f..a4841d633 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -188,7 +188,8 @@ public class LocalRepoActivity extends Activity { .replaceAll("ssid=[^?]*", "") .toUpperCase(Locale.ENGLISH); Log.i("QRURI", qrUriString); - new QrGenAsyncTask(this, R.id.repoQrCode).execute(qrUriString); + if (Build.VERSION.SDK_INT >= 8) // zxing requires >= 8 + new QrGenAsyncTask(this, R.id.repoQrCode).execute(qrUriString); TextView wifiNetworkNameTextView = (TextView) findViewById(R.id.wifiNetworkName); wifiNetworkNameTextView.setText(FDroidApp.ssid); diff --git a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java index a4889ec09..a776fabf5 100644 --- a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java @@ -2,11 +2,8 @@ package org.fdroid.fdroid.views; import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; +import android.content.*; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; @@ -73,7 +70,8 @@ public class QrWizardDownloadActivity extends Activity { qrString += "://" + FDroidApp.ipAddressString; qrString += ":" + FDroidApp.port; - new QrGenAsyncTask(this, R.id.qrWizardImage).execute(qrString); + if (Build.VERSION.SDK_INT >= 8) // zxing requires >= 8 + new QrGenAsyncTask(this, R.id.qrWizardImage).execute(qrString); Log.i(TAG, "qr: " + qrString); TextView wifiNetworkName = (TextView) findViewById(R.id.qrWifiNetworkName); diff --git a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java index 2f667a1ea..8e08e2667 100644 --- a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java @@ -2,12 +2,10 @@ package org.fdroid.fdroid.views; import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; +import android.content.*; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.Build; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -92,12 +90,11 @@ public class QrWizardWifiNetworkActivity extends Activity { if (wifiInfo.getHiddenSSID()) qrString += ";H:true"; qrString += ";;"; - new QrGenAsyncTask(this, R.id.qrWizardImage).execute(qrString); - Log.i(TAG, "qr: " + qrString); + if (Build.VERSION.SDK_INT >= 8) // zxing requires >= 8 + new QrGenAsyncTask(this, R.id.qrWizardImage).execute(qrString); TextView wifiNetworkName = (TextView) findViewById(R.id.qrWifiNetworkName); wifiNetworkName.setText(wifiInfo.getSSID()); - Log.i(TAG, "wifi network name: " + wifiInfo.getSSID()); } } From 584152d2fa25b32c304e7e5899a0d0b2365bbfb3 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 6 May 2014 23:13:59 -0400 Subject: [PATCH 03/14] store "App Label" for Installed Apps, to show in Local Repo setup Most people are going to know the "label" i.e. the display name rather than the packageName/id. So also store the label in the database and make it accessible via InstalledAppsProvider so SelectLocalAppsFragment can show a list of friendly names rather than packageNames. --- .../fdroid/fdroid/PackageAddedReceiver.java | 7 +++++- .../fdroid/PackageUpgradedReceiver.java | 10 ++++++-- src/org/fdroid/fdroid/data/DBHelper.java | 18 ++++++++++---- .../fdroid/data/InstalledAppCacheUpdater.java | 2 ++ .../fdroid/data/InstalledAppProvider.java | 24 ++++++++++++++++++- .../fragments/SelectLocalAppsFragment.java | 9 +++---- .../org/fdroid/fdroid/FDroidProviderTest.java | 3 +++ 7 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/org/fdroid/fdroid/PackageAddedReceiver.java b/src/org/fdroid/fdroid/PackageAddedReceiver.java index 0f683e3ae..e55bbd91f 100644 --- a/src/org/fdroid/fdroid/PackageAddedReceiver.java +++ b/src/org/fdroid/fdroid/PackageAddedReceiver.java @@ -21,7 +21,10 @@ package org.fdroid.fdroid; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.util.Log; import org.fdroid.fdroid.data.InstalledAppProvider; @@ -44,10 +47,12 @@ public class PackageAddedReceiver extends PackageReceiver { Log.d("FDroid", "Inserting installed app info for '" + appId + "' (v" + info.versionCode + ")"); Uri uri = InstalledAppProvider.getContentUri(); - ContentValues values = new ContentValues(3); + ContentValues values = new ContentValues(4); values.put(InstalledAppProvider.DataColumns.APP_ID, appId); values.put(InstalledAppProvider.DataColumns.VERSION_CODE, info.versionCode); values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName); + values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, + InstalledAppProvider.getApplicationLabel(context, appId)); context.getContentResolver().insert(uri, values); } diff --git a/src/org/fdroid/fdroid/PackageUpgradedReceiver.java b/src/org/fdroid/fdroid/PackageUpgradedReceiver.java index 6f4057dca..699c40133 100644 --- a/src/org/fdroid/fdroid/PackageUpgradedReceiver.java +++ b/src/org/fdroid/fdroid/PackageUpgradedReceiver.java @@ -21,9 +21,13 @@ package org.fdroid.fdroid; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.util.Log; + import org.fdroid.fdroid.data.InstalledAppProvider; /** @@ -46,10 +50,12 @@ public class PackageUpgradedReceiver extends PackageReceiver { Log.d("FDroid", "Updating installed app info for '" + appId + "' to v" + info.versionCode + " (" + info.versionName + ")"); Uri uri = InstalledAppProvider.getContentUri(); - ContentValues values = new ContentValues(1); - values.put(InstalledAppProvider.DataColumns.APP_ID, info.packageName); + ContentValues values = new ContentValues(4); + values.put(InstalledAppProvider.DataColumns.APP_ID, appId); values.put(InstalledAppProvider.DataColumns.VERSION_CODE, info.versionCode); values.put(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName); + values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, + InstalledAppProvider.getApplicationLabel(context, appId)); context.getContentResolver().insert(uri, values); } diff --git a/src/org/fdroid/fdroid/data/DBHelper.java b/src/org/fdroid/fdroid/data/DBHelper.java index 4d81bb4b4..229900d4b 100644 --- a/src/org/fdroid/fdroid/data/DBHelper.java +++ b/src/org/fdroid/fdroid/data/DBHelper.java @@ -91,12 +91,13 @@ public class DBHelper extends SQLiteOpenHelper { public static final String TABLE_INSTALLED_APP = "fdroid_installedApp"; private static final String CREATE_TABLE_INSTALLED_APP = "CREATE TABLE " + TABLE_INSTALLED_APP + " ( " - + "appId TEXT NOT NULL PRIMARY KEY, " - + "versionCode INT NOT NULL, " - + "versionName TEXT NOT NULL " + + InstalledAppProvider.DataColumns.APP_ID + " TEXT NOT NULL PRIMARY KEY, " + + InstalledAppProvider.DataColumns.VERSION_CODE + " INT NOT NULL, " + + InstalledAppProvider.DataColumns.VERSION_NAME + " TEXT NOT NULL, " + + InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL " + " );"; - private static final int DB_VERSION = 44; + private static final int DB_VERSION = 45; private Context context; @@ -249,6 +250,7 @@ public class DBHelper extends SQLiteOpenHelper { addLastUpdatedToRepo(db, oldVersion); renameRepoId(db, oldVersion); populateRepoNames(db, oldVersion); + upgradeInstalledApp(db, oldVersion); if (oldVersion < 43) createInstalledApp(db); } @@ -398,6 +400,14 @@ public class DBHelper extends SQLiteOpenHelper { db.execSQL(CREATE_TABLE_INSTALLED_APP); } + private void upgradeInstalledApp(SQLiteDatabase db, int oldVersion) { + if (oldVersion < 45) { + Log.i(TAG, "upgradeInstalledApp"); + // just wipe it out, so it'll get rebuilt from scratch + db.execSQL("DELETE FROM fdroid_installedApp;"); + } + } + private static boolean columnExists(SQLiteDatabase db, String table, String column) { return (db.rawQuery( "select * from " + table + " limit 0,1", null ) diff --git a/src/org/fdroid/fdroid/data/InstalledAppCacheUpdater.java b/src/org/fdroid/fdroid/data/InstalledAppCacheUpdater.java index edb4ae5b3..a9fbf15fe 100644 --- a/src/org/fdroid/fdroid/data/InstalledAppCacheUpdater.java +++ b/src/org/fdroid/fdroid/data/InstalledAppCacheUpdater.java @@ -135,6 +135,8 @@ public class InstalledAppCacheUpdater { .withValue(InstalledAppProvider.DataColumns.APP_ID, info.packageName) .withValue(InstalledAppProvider.DataColumns.VERSION_CODE, info.versionCode) .withValue(InstalledAppProvider.DataColumns.VERSION_NAME, info.versionName) + .withValue(InstalledAppProvider.DataColumns.APPLICATION_LABEL, + InstalledAppProvider.getApplicationLabel(context, info.packageName)) .build(); ops.add(op); } diff --git a/src/org/fdroid/fdroid/data/InstalledAppProvider.java b/src/org/fdroid/fdroid/data/InstalledAppProvider.java index 8e4b8136e..640a1948b 100644 --- a/src/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/src/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -3,9 +3,14 @@ package org.fdroid.fdroid.data; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources.NotFoundException; import android.database.Cursor; import android.net.Uri; import android.util.Log; + import org.fdroid.fdroid.R; import java.util.HashMap; @@ -49,8 +54,11 @@ public class InstalledAppProvider extends FDroidProvider { public static final String APP_ID = "appId"; public static final String VERSION_CODE = "versionCode"; public static final String VERSION_NAME = "versionName"; + public static final String APPLICATION_LABEL = "applicationLabel"; - public static String[] ALL = { _ID, APP_ID, VERSION_CODE, VERSION_NAME }; + public static String[] ALL = { + _ID, APP_ID, VERSION_CODE, VERSION_NAME, APPLICATION_LABEL, + }; } @@ -71,6 +79,20 @@ public class InstalledAppProvider extends FDroidProvider { return Uri.withAppendedPath(getContentUri(), appId); } + public static String getApplicationLabel(Context context, String packageName) { + PackageManager pm = context.getPackageManager(); + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + return appInfo.loadLabel(pm).toString(); + } catch (NameNotFoundException e) { + e.printStackTrace(); + } catch (NotFoundException e) { + Log.d("InstalledAppProvider", "getApplicationLabel: " + e.getMessage()); + } + return packageName; // all else fails, return id + } + @Override protected String getTableName() { return DBHelper.TABLE_INSTALLED_APP; diff --git a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java index b9a6b7ce8..58d15c363 100644 --- a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java @@ -60,6 +60,7 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb android.R.layout.simple_list_item_activated_1, null, new String[] { + InstalledAppProvider.DataColumns.APPLICATION_LABEL, InstalledAppProvider.DataColumns.APP_ID, }, new int[] { @@ -91,8 +92,8 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb if (mActionMode == null) mActionMode = selectLocalAppsActivity .startActionMode(selectLocalAppsActivity.mActionModeCallback); - Cursor cursor = (Cursor) l.getAdapter().getItem(position); - String packageName = cursor.getString(1); + Cursor c = (Cursor) l.getAdapter().getItem(position); + String packageName = c.getString(c.getColumnIndex(DataColumns.APP_ID)); if (FDroidApp.selectedApps.contains(packageName)) { FDroidApp.selectedApps.remove(packageName); } else { @@ -108,7 +109,7 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb InstalledAppProvider.DataColumns.ALL, null, null, - InstalledAppProvider.DataColumns.APP_ID); + InstalledAppProvider.DataColumns.APPLICATION_LABEL); return loader; } @@ -123,7 +124,7 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb Cursor c = ((Cursor) listView.getItemAtPosition(i)); String packageName = c.getString(c.getColumnIndex(DataColumns.APP_ID)); if (TextUtils.equals(packageName, fdroid)) { - listView.setItemChecked(i, true); + listView.setItemChecked(i, true); // always include FDroid } else { for (String selected : FDroidApp.selectedApps) { if (TextUtils.equals(packageName, selected)) { diff --git a/test/src/org/fdroid/fdroid/FDroidProviderTest.java b/test/src/org/fdroid/fdroid/FDroidProviderTest.java index 1be7eec3d..4102b9690 100644 --- a/test/src/org/fdroid/fdroid/FDroidProviderTest.java +++ b/test/src/org/fdroid/fdroid/FDroidProviderTest.java @@ -9,9 +9,11 @@ import android.net.Uri; import android.os.Build; import android.provider.ContactsContract; import android.test.ProviderTestCase2MockContext; + import mock.MockContextEmptyComponents; import mock.MockContextSwappableComponents; import mock.MockFDroidResources; + import org.fdroid.fdroid.data.*; import java.util.List; @@ -151,6 +153,7 @@ public abstract class FDroidProviderTest extends Provi InstalledAppProvider.DataColumns.APP_ID, InstalledAppProvider.DataColumns.VERSION_CODE, InstalledAppProvider.DataColumns.VERSION_NAME, + InstalledAppProvider.DataColumns.APPLICATION_LABEL, }; Cursor cursor = getMockContentResolver().query(uri, projection, null, null, null); From 60f5a1441c1bbd0808df2d0cbfdd3ee7c79dc99d Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 8 May 2014 05:52:29 +0930 Subject: [PATCH 04/14] Installed apps by application label by default, and sort order can be chosen. Also prevented crash due to lack of applicationLabel field. --- src/org/fdroid/fdroid/data/DBHelper.java | 18 ++++++++++-------- .../fdroid/data/InstalledAppProvider.java | 6 +++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/org/fdroid/fdroid/data/DBHelper.java b/src/org/fdroid/fdroid/data/DBHelper.java index 229900d4b..f0e8ab00d 100644 --- a/src/org/fdroid/fdroid/data/DBHelper.java +++ b/src/org/fdroid/fdroid/data/DBHelper.java @@ -97,7 +97,7 @@ public class DBHelper extends SQLiteOpenHelper { + InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL " + " );"; - private static final int DB_VERSION = 45; + private static final int DB_VERSION = 46; private Context context; @@ -250,12 +250,11 @@ public class DBHelper extends SQLiteOpenHelper { addLastUpdatedToRepo(db, oldVersion); renameRepoId(db, oldVersion); populateRepoNames(db, oldVersion); - upgradeInstalledApp(db, oldVersion); - if (oldVersion < 43) createInstalledApp(db); + addAppLabelToInstalledCache(db, oldVersion); } - /** + /** * Migrate repo list to new structure. (No way to change primary * key in sqlite - table must be recreated). */ @@ -400,11 +399,14 @@ public class DBHelper extends SQLiteOpenHelper { db.execSQL(CREATE_TABLE_INSTALLED_APP); } - private void upgradeInstalledApp(SQLiteDatabase db, int oldVersion) { + private void addAppLabelToInstalledCache(SQLiteDatabase db, int oldVersion) { if (oldVersion < 45) { - Log.i(TAG, "upgradeInstalledApp"); - // just wipe it out, so it'll get rebuilt from scratch - db.execSQL("DELETE FROM fdroid_installedApp;"); + Log.i(TAG, "Adding applicationLabel to installed app table. " + + "Turns out we will need to repopulate the cache after doing this, " + + "so just dropping and recreating the table (instead of altering and adding a column). " + + "This will force the entire cache to be rebuilt, including app names."); + db.execSQL("DROP TABLE fdroid_installedApp;"); + createInstalledApp(db); } } diff --git a/src/org/fdroid/fdroid/data/InstalledAppProvider.java b/src/org/fdroid/fdroid/data/InstalledAppProvider.java index 640a1948b..fea0abe00 100644 --- a/src/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/src/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -118,6 +118,10 @@ public class InstalledAppProvider extends FDroidProvider { @Override public Cursor query(Uri uri, String[] projection, String customSelection, String[] selectionArgs, String sortOrder) { + if (sortOrder == null) { + sortOrder = DataColumns.APPLICATION_LABEL; + } + QuerySelection selection = new QuerySelection(customSelection, selectionArgs); switch (matcher.match(uri)) { case CODE_LIST: @@ -133,7 +137,7 @@ public class InstalledAppProvider extends FDroidProvider { throw new UnsupportedOperationException(message); } - Cursor cursor = read().query(getTableName(), projection, selection.getSelection(), selection.getArgs(), null, null, null); + Cursor cursor = read().query(getTableName(), projection, selection.getSelection(), selection.getArgs(), null, null, sortOrder); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; } From 5e02404d6ad4ef86b481fa1d76317ac6828bbe17 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 8 May 2014 06:46:50 +0930 Subject: [PATCH 05/14] Added getSearchUri() to InstalledAppProvider This allows for searching installed app names for ones which match a string. It searches based on the "label" as declared in the manifest in the tag as "android:label". --- .../fdroid/data/InstalledAppProvider.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/org/fdroid/fdroid/data/InstalledAppProvider.java b/src/org/fdroid/fdroid/data/InstalledAppProvider.java index fea0abe00..aa584ffad 100644 --- a/src/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/src/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -64,10 +64,14 @@ public class InstalledAppProvider extends FDroidProvider { private static final String PROVIDER_NAME = "InstalledAppProvider"; + private static final String PATH_SEARCH = "search"; + private static final int CODE_SEARCH = CODE_SINGLE + 1; + private static final UriMatcher matcher = new UriMatcher(-1); static { matcher.addURI(getAuthority(), null, CODE_LIST); + matcher.addURI(getAuthority(), PATH_SEARCH + "/*", CODE_SEARCH); matcher.addURI(getAuthority(), "*", CODE_SINGLE); } @@ -79,6 +83,13 @@ public class InstalledAppProvider extends FDroidProvider { return Uri.withAppendedPath(getContentUri(), appId); } + public static Uri getSearchUri(String keywords) { + return getContentUri().buildUpon() + .appendPath(PATH_SEARCH) + .appendPath(keywords) + .build(); + } + public static String getApplicationLabel(Context context, String packageName) { PackageManager pm = context.getPackageManager(); ApplicationInfo appInfo; @@ -116,6 +127,10 @@ public class InstalledAppProvider extends FDroidProvider { return new QuerySelection("appId = ?", new String[] { appId } ); } + private QuerySelection querySearch(String keywords) { + return new QuerySelection("applicationLabel LIKE ?", new String[] { "%" + keywords + "%" } ); + } + @Override public Cursor query(Uri uri, String[] projection, String customSelection, String[] selectionArgs, String sortOrder) { if (sortOrder == null) { @@ -131,6 +146,10 @@ public class InstalledAppProvider extends FDroidProvider { selection = selection.add(queryApp(uri.getLastPathSegment())); break; + case CODE_SEARCH: + selection = selection.add(querySearch(uri.getLastPathSegment())); + break; + default: String message = "Invalid URI for installed app content provider: " + uri; Log.e("FDroid", message); From a6de6b2932e777a7492dd86a85bb568d07a1394e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 19:18:10 -0400 Subject: [PATCH 06/14] use two text item layout with label and packageName for SelectLocalApps Having the packageName as the minor text is useful for figuring out who the developer of the app is. --- .../fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java index 58d15c363..4342719a5 100644 --- a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java @@ -57,7 +57,7 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); SimpleCursorAdapter adapter = new SimpleCursorAdapter(getActivity(), - android.R.layout.simple_list_item_activated_1, + android.R.layout.simple_list_item_activated_2, null, new String[] { InstalledAppProvider.DataColumns.APPLICATION_LABEL, @@ -65,6 +65,7 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb }, new int[] { android.R.id.text1, + android.R.id.text2, }, 0); setListAdapter(adapter); From 914149aad29ca9a408326e65633dcc8103561ce7 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 20:48:02 -0400 Subject: [PATCH 07/14] live filtering of InstalledApps for setting up Local Repo This implements live filtering in a SearchView so that it is easy to search for the apps you want to include in your Local Repo. This requires some newer stuff, so I switched it to the android-11 Activity until appcompat-v7 is included. All this functionality will work fine with appcompat-v7. --- res/menu/select_local_apps_action_mode.xml | 5 ++ res/menu/select_local_apps_activity.xml | 9 ++- .../fdroid/views/SelectLocalAppsActivity.java | 22 +++++--- .../fragments/SelectLocalAppsFragment.java | 56 +++++++++++++++---- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/res/menu/select_local_apps_action_mode.xml b/res/menu/select_local_apps_action_mode.xml index 2550ebcfe..89d8f8e7b 100644 --- a/res/menu/select_local_apps_action_mode.xml +++ b/res/menu/select_local_apps_action_mode.xml @@ -1,5 +1,10 @@ + + \ No newline at end of file diff --git a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java index ff0ad5445..1ccbd0281 100644 --- a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java +++ b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java @@ -2,13 +2,11 @@ package org.fdroid.fdroid.views; import android.annotation.TargetApi; +import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; +import android.view.*; +import android.widget.SearchView; import org.fdroid.fdroid.PreferencesActivity; import org.fdroid.fdroid.R; @@ -16,9 +14,10 @@ import org.fdroid.fdroid.views.fragments.SelectLocalAppsFragment; @TargetApi(11) // TODO replace with appcompat-v7 -public class SelectLocalAppsActivity extends FragmentActivity { +public class SelectLocalAppsActivity extends Activity { private static final String TAG = "SelectLocalAppsActivity"; private SelectLocalAppsFragment selectLocalAppsFragment = null; + private SearchView searchView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -30,13 +29,15 @@ public class SelectLocalAppsActivity extends FragmentActivity { protected void onResume() { super.onResume(); if (selectLocalAppsFragment == null) - selectLocalAppsFragment = (SelectLocalAppsFragment) getSupportFragmentManager().findFragmentById( - R.id.fragment_app_list); + selectLocalAppsFragment = (SelectLocalAppsFragment) getFragmentManager() + .findFragmentById(R.id.fragment_app_list); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.select_local_apps_activity, menu); + searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); + searchView.setOnQueryTextListener(selectLocalAppsFragment); return true; } @@ -47,6 +48,10 @@ public class SelectLocalAppsActivity extends FragmentActivity { setResult(RESULT_CANCELED); finish(); return true; + case R.id.action_search: + SearchView searchView = (SearchView) item.getActionView(); + searchView.setIconified(false); + return true; case R.id.action_settings: startActivity(new Intent(this, PreferencesActivity.class)); return true; @@ -60,6 +65,7 @@ public class SelectLocalAppsActivity extends FragmentActivity { public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.select_local_apps_action_mode, menu); + menu.findItem(R.id.action_search).setActionView(searchView); return true; } diff --git a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java index 4342719a5..182d95dcb 100644 --- a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java @@ -15,17 +15,19 @@ limitations under the License. package org.fdroid.fdroid.views.fragments; import android.annotation.TargetApi; +import android.app.ListFragment; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.CursorLoader; +import android.content.Loader; import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.ListFragment; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.widget.SimpleCursorAdapter; import android.text.TextUtils; import android.view.ActionMode; import android.view.View; import android.widget.ListView; +import android.widget.SearchView.OnQueryTextListener; +import android.widget.SimpleCursorAdapter; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.R; @@ -35,18 +37,20 @@ import org.fdroid.fdroid.views.SelectLocalAppsActivity; import java.util.HashSet; -public class SelectLocalAppsFragment extends ListFragment implements LoaderCallbacks { +//TODO replace with appcompat-v7 +@TargetApi(11) +public class SelectLocalAppsFragment extends ListFragment + implements LoaderCallbacks, OnQueryTextListener { private SelectLocalAppsActivity selectLocalAppsActivity; private ActionMode mActionMode = null; + private String mCurrentFilterString; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } - @TargetApi(11) - // TODO replace with appcompat-v7 @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -69,7 +73,7 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb }, 0); setListAdapter(adapter); - setListShown(false); + setListShown(false); // start out with a progress indicator // either reconnect with an existing loader or start a new one getLoaderManager().initLoader(0, null, this); @@ -86,8 +90,6 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb } } - @TargetApi(11) - // TODO replace with appcompat-v7 @Override public void onListItemClick(ListView l, View v, int position, long id) { if (mActionMode == null) @@ -104,9 +106,15 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb @Override public CursorLoader onCreateLoader(int id, Bundle args) { + Uri baseUri; + if (TextUtils.isEmpty(mCurrentFilterString)) { + baseUri = InstalledAppProvider.getContentUri(); + } else { + baseUri = InstalledAppProvider.getSearchUri(mCurrentFilterString); + } CursorLoader loader = new CursorLoader( this.getActivity(), - InstalledAppProvider.getContentUri(), + baseUri, InstalledAppProvider.DataColumns.ALL, null, null, @@ -146,4 +154,28 @@ public class SelectLocalAppsFragment extends ListFragment implements LoaderCallb public void onLoaderReset(Loader loader) { ((SimpleCursorAdapter) this.getListAdapter()).swapCursor(null); } + + @Override + public boolean onQueryTextChange(String newText) { + String newFilter = !TextUtils.isEmpty(newText) ? newText : null; + if (mCurrentFilterString == null && newFilter == null) { + return true; + } + if (mCurrentFilterString != null && mCurrentFilterString.equals(newFilter)) { + return true; + } + mCurrentFilterString = newFilter; + getLoaderManager().restartLoader(0, null, this); + return true; + } + + @Override + public boolean onQueryTextSubmit(String query) { + // this is not needed since we respond to every change in text + return true; + } + + public String getCurrentFilterString() { + return mCurrentFilterString; + } } From 8d3d32596794a573fedc95ba94677ab7d56dac5a Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 21:30:59 -0400 Subject: [PATCH 08/14] apply light/dark theme to all Local Repo related Activities --- src/org/fdroid/fdroid/views/LocalRepoActivity.java | 1 + src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java | 1 + src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java | 1 + src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java | 2 ++ 4 files changed, 5 insertions(+) diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index a4841d633..b54904154 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -42,6 +42,7 @@ public class LocalRepoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + ((FDroidApp) getApplication()).applyTheme(this); setContentView(R.layout.local_repo_activity); repoSwitch = (ToggleButton) findViewById(R.id.repoSwitch); diff --git a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java index a776fabf5..ffe90fae9 100644 --- a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java @@ -24,6 +24,7 @@ public class QrWizardDownloadActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + ((FDroidApp) getApplication()).applyTheme(this); setContentView(R.layout.qr_wizard_activity); TextView instructions = (TextView) findViewById(R.id.qrWizardInstructions); instructions.setText(R.string.qr_wizard_download_instructions); diff --git a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java index 8e08e2667..6e933de92 100644 --- a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java @@ -31,6 +31,7 @@ public class QrWizardWifiNetworkActivity extends Activity { wifiManager.setWifiEnabled(true); FDroidApp.startLocalRepoService(this); + ((FDroidApp) getApplication()).applyTheme(this); setContentView(R.layout.qr_wizard_activity); TextView instructions = (TextView) findViewById(R.id.qrWizardInstructions); instructions.setText(R.string.qr_wizard_wifi_network_instructions); diff --git a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java index 1ccbd0281..71fc8ee9c 100644 --- a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java +++ b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java @@ -8,6 +8,7 @@ import android.os.Bundle; import android.view.*; import android.widget.SearchView; +import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.PreferencesActivity; import org.fdroid.fdroid.R; import org.fdroid.fdroid.views.fragments.SelectLocalAppsFragment; @@ -22,6 +23,7 @@ public class SelectLocalAppsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + ((FDroidApp) getApplication()).applyTheme(this); setContentView(R.layout.select_local_apps_activity); } From 7401366ac9b2221cb40005b5454c0e069b0fa393 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 22:03:35 -0400 Subject: [PATCH 09/14] quick improvement of LocalRepoActivity layout This is meant to try to make the on/off state of the webserver that serves the local repo more apparent. --- res/layout/local_repo_activity.xml | 35 ++++++++++++++++--- res/values/strings.xml | 3 ++ .../fdroid/views/LocalRepoActivity.java | 30 +++++++++------- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/res/layout/local_repo_activity.xml b/res/layout/local_repo_activity.xml index 81ba324ca..17908b176 100644 --- a/res/layout/local_repo_activity.xml +++ b/res/layout/local_repo_activity.xml @@ -4,23 +4,50 @@ android:layout_height="fill_parent" android:orientation="vertical" > - + + + android:layout_height="wrap_content" + android:gravity="center" + android:padding="15dp" + android:text="@string/touch_to_turn_on_local_repo" /> + + + + + + + Your local FDroid repo is accessible. Setup Local Repo Touch to setup your local repo. + Touch to turn on your local repo. + Touch to turn off your local repo. Updating… Update Repo Deleting current repo… @@ -168,6 +170,7 @@ icon Fingerprint: WiFi Network: + Sharing URL: Enable WiFi Enabling WiFi… To connect to other people\'s devices, make sure both devices are on the same WiFi network. Then either type the URL above into F-Droid, or scan this QR Code: diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index b54904154..d71d30129 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -19,9 +19,7 @@ import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import android.view.*; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ToggleButton; +import android.widget.*; import org.fdroid.fdroid.*; import org.fdroid.fdroid.net.WifiStateChangeService; @@ -33,7 +31,8 @@ public class LocalRepoActivity extends Activity { private ProgressDialog repoProgress; private WifiManager wifiManager; - private ToggleButton repoSwitch; + private Button enableWifiButton; + private CheckBox repoSwitch; private int SET_IP_ADDRESS = 7345; private int UPDATE_REPO = 7346; @@ -45,7 +44,8 @@ public class LocalRepoActivity extends Activity { ((FDroidApp) getApplication()).applyTheme(this); setContentView(R.layout.local_repo_activity); - repoSwitch = (ToggleButton) findViewById(R.id.repoSwitch); + enableWifiButton = (Button) findViewById(R.id.enable_wifi); + repoSwitch = (CheckBox) findViewById(R.id.repoSwitch); wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); } @@ -80,14 +80,17 @@ public class LocalRepoActivity extends Activity { if (wifiState == WifiManager.WIFI_STATE_ENABLED) { setUIFromWifi(); wireRepoSwitchToWebServer(); + repoSwitch.setVisibility(View.VISIBLE); + enableWifiButton.setVisibility(View.GONE); } else { repoSwitch.setChecked(false); - repoSwitch.setText(R.string.enable_wifi); - repoSwitch.setTextOn(getString(R.string.enabling_wifi)); - repoSwitch.setTextOff(getString(R.string.enable_wifi)); - repoSwitch.setOnClickListener(new View.OnClickListener() { + repoSwitch.setVisibility(View.GONE); + enableWifiButton.setVisibility(View.VISIBLE); + enableWifiButton.setText(R.string.enable_wifi); + enableWifiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + enableWifiButton.setText(R.string.enabling_wifi); wifiManager.setWifiEnabled(true); /* * Once the wifi is connected to a network, then @@ -161,8 +164,10 @@ public class LocalRepoActivity extends Activity { public void onClick(View v) { if (repoSwitch.isChecked()) { FDroidApp.startLocalRepoService(LocalRepoActivity.this); + repoSwitch.setText(R.string.local_repo_running); } else { FDroidApp.stopLocalRepoService(LocalRepoActivity.this); + repoSwitch.setText(R.string.touch_to_turn_on_local_repo); } } }); @@ -174,9 +179,8 @@ public class LocalRepoActivity extends Activity { return; // the fingerprint is not useful on the button label String buttonLabel = FDroidApp.repo.address.replaceAll("\\?.*$", ""); - repoSwitch.setText(buttonLabel); - repoSwitch.setTextOn(buttonLabel); - repoSwitch.setTextOff(buttonLabel); + TextView sharingUriTextView = (TextView) findViewById(R.id.sharing_uri); + sharingUriTextView.setText(buttonLabel); /* * Set URL to UPPER for compact QR Code, FDroid will translate it back. * Remove the SSID from the query string since SSIDs are case-sensitive. @@ -192,7 +196,7 @@ public class LocalRepoActivity extends Activity { if (Build.VERSION.SDK_INT >= 8) // zxing requires >= 8 new QrGenAsyncTask(this, R.id.repoQrCode).execute(qrUriString); - TextView wifiNetworkNameTextView = (TextView) findViewById(R.id.wifiNetworkName); + TextView wifiNetworkNameTextView = (TextView) findViewById(R.id.wifi_network); wifiNetworkNameTextView.setText(FDroidApp.ssid); TextView fingerprintTextView = (TextView) findViewById(R.id.fingerprint); From 2256cd00e1361717b6f13ee93fd215e9ab29e416 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 22:24:31 -0400 Subject: [PATCH 10/14] start/stop Local Repo from any Activity This forces the use of the Application's Context, so we can be sure the webserver will run as long as FDroid is running. It also checks to make sure whether the webserver is running before trying to start it. --- src/org/fdroid/fdroid/FDroidApp.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 899162b20..1e120dd97 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -122,7 +122,7 @@ public class FDroidApp extends Application { // it is more deterministic as to when this gets called... Preferences.setup(this); - //Apply the Google PRNG fixes to properly seed SecureRandom + // Apply the Google PRNG fixes to properly seed SecureRandom PRNGFixes.apply(); localRepo = new LocalRepoManager(getApplicationContext()); @@ -301,14 +301,17 @@ public class FDroidApp extends Application { }; public static void startLocalRepoService(Context context) { - context.bindService(new Intent(context, LocalRepoService.class), - serviceConnection, Context.BIND_AUTO_CREATE); - localRepoServiceIsBound = true; + if (!localRepoServiceIsBound) { + Context app = context.getApplicationContext(); + app.bindService(new Intent(app, LocalRepoService.class), + serviceConnection, Context.BIND_AUTO_CREATE); + localRepoServiceIsBound = true; + } } public static void stopLocalRepoService(Context context) { if (localRepoServiceIsBound) { - context.unbindService(serviceConnection); + context.getApplicationContext().unbindService(serviceConnection); localRepoServiceIsBound = false; } } From 16399b760be576b5b73049b04bfcf9567d1c091e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 22:30:26 -0400 Subject: [PATCH 11/14] start the local repo webserver by default when going to LocalRepoActivity To make it dead simple to swap repos when going to the Local Repos screen. --- .../fdroid/fdroid/views/LocalRepoActivity.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index d71d30129..84b7a912f 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -53,6 +53,11 @@ public class LocalRepoActivity extends Activity { public void onResume() { super.onResume(); resetNetworkInfo(); + + // start repo by default + setRepoSwitchChecked(true); + FDroidApp.startLocalRepoService(LocalRepoActivity.this); + LocalBroadcastManager.getInstance(this).registerReceiver(onWifiChange, new IntentFilter(WifiStateChangeService.BROADCAST)); // if no local repo exists, create one with only FDroid in it @@ -162,17 +167,25 @@ public class LocalRepoActivity extends Activity { repoSwitch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + setRepoSwitchChecked(repoSwitch.isChecked()); if (repoSwitch.isChecked()) { FDroidApp.startLocalRepoService(LocalRepoActivity.this); - repoSwitch.setText(R.string.local_repo_running); } else { FDroidApp.stopLocalRepoService(LocalRepoActivity.this); - repoSwitch.setText(R.string.touch_to_turn_on_local_repo); } } }); } + private void setRepoSwitchChecked(boolean checked) { + repoSwitch.setChecked(checked); + if (checked) { + repoSwitch.setText(R.string.local_repo_running); + } else { + repoSwitch.setText(R.string.touch_to_turn_on_local_repo); + } + } + @TargetApi(14) private void setUIFromWifi() { if (TextUtils.isEmpty(FDroidApp.repo.address)) From 107eab5eaccc3e01d208581856cfe42d6d526121 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 7 May 2014 23:51:53 -0400 Subject: [PATCH 12/14] Local Repo webserver turns itself off if it was automatically started When you visit LocalRepoActivity, the swapping webserver is automatically turned on, since it is required for any swapping to happen. When it was automatically turned on, it will automatically turn itself off after 15 minutes to make sure that it doesn't stay running forever. If the user manually turns it off, that cancels the automatic stop. --- .../fdroid/localrepo/LocalRepoService.java | 10 ++++++ .../fdroid/views/LocalRepoActivity.java | 36 ++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java index cff716322..c9d6511c6 100644 --- a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java +++ b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java @@ -34,6 +34,10 @@ import java.util.Random; public class LocalRepoService extends Service { private static final String TAG = "LocalRepoService"; + public static final String STATE = "org.fdroid.fdroid.action.LOCAL_REPO_STATE"; + public static final String STARTED = "org.fdroid.fdroid.category.LOCAL_REPO_STARTED"; + public static final String STOPPED = "org.fdroid.fdroid.category.LOCAL_REPO_STOPPED"; + private NotificationManager notificationManager; // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. @@ -148,6 +152,9 @@ public class LocalRepoService extends Service { } }; new Thread(webServer).start(); + Intent intent = new Intent(STATE); + intent.putExtra(STATE, STARTED); + LocalBroadcastManager.getInstance(LocalRepoService.this).sendBroadcast(intent); } private void stopWebServer() { @@ -158,5 +165,8 @@ public class LocalRepoService extends Service { Message msg = webServerThreadHandler.obtainMessage(); msg.obj = webServerThreadHandler.getLooper().getThread().getName() + " says stop"; webServerThreadHandler.sendMessage(msg); + Intent intent = new Intent(STATE); + intent.putExtra(STATE, STOPPED); + LocalBroadcastManager.getInstance(LocalRepoService.this).sendBroadcast(intent); } } diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index 84b7a912f..5e1ffc23a 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -22,9 +22,12 @@ import android.view.*; import android.widget.*; import org.fdroid.fdroid.*; +import org.fdroid.fdroid.localrepo.LocalRepoService; import org.fdroid.fdroid.net.WifiStateChangeService; import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; public class LocalRepoActivity extends Activity { private static final String TAG = "LocalRepoActivity"; @@ -34,6 +37,8 @@ public class LocalRepoActivity extends Activity { private Button enableWifiButton; private CheckBox repoSwitch; + private Timer stopTimer; + private int SET_IP_ADDRESS = 7345; private int UPDATE_REPO = 7346; @@ -54,23 +59,34 @@ public class LocalRepoActivity extends Activity { super.onResume(); resetNetworkInfo(); - // start repo by default - setRepoSwitchChecked(true); - FDroidApp.startLocalRepoService(LocalRepoActivity.this); - LocalBroadcastManager.getInstance(this).registerReceiver(onWifiChange, new IntentFilter(WifiStateChangeService.BROADCAST)); + LocalBroadcastManager.getInstance(this).registerReceiver(onLocalRepoChange, + new IntentFilter(LocalRepoService.STATE)); // if no local repo exists, create one with only FDroid in it if (!FDroidApp.localRepo.xmlIndex.exists()) new UpdateAsyncTask(this, new String[] { getPackageName(), }).execute(); + + // start repo by default + FDroidApp.startLocalRepoService(LocalRepoActivity.this); + // automatically turn off after 15 minutes + stopTimer = new Timer(); + stopTimer.schedule(new TimerTask() { + + @Override + public void run() { + FDroidApp.stopLocalRepoService(LocalRepoActivity.this); + } + }, 900000); // 15 minutes } @Override public void onPause() { super.onPause(); LocalBroadcastManager.getInstance(this).unregisterReceiver(onWifiChange); + LocalBroadcastManager.getInstance(this).unregisterReceiver(onLocalRepoChange); } private BroadcastReceiver onWifiChange = new BroadcastReceiver() { @@ -80,6 +96,17 @@ public class LocalRepoActivity extends Activity { } }; + private BroadcastReceiver onLocalRepoChange = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent i) { + String state = i.getStringExtra(LocalRepoService.STATE); + if (state != null && state.equals(LocalRepoService.STARTED)) + setRepoSwitchChecked(true); + else + setRepoSwitchChecked(false); + } + }; + private void resetNetworkInfo() { int wifiState = wifiManager.getWifiState(); if (wifiState == WifiManager.WIFI_STATE_ENABLED) { @@ -172,6 +199,7 @@ public class LocalRepoActivity extends Activity { FDroidApp.startLocalRepoService(LocalRepoActivity.this); } else { FDroidApp.stopLocalRepoService(LocalRepoActivity.this); + stopTimer.cancel(); // disable automatic stop } } }); From efacc22c101cf403f6d2c08c731820f4427cfb18 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 8 May 2014 00:56:09 -0400 Subject: [PATCH 13/14] include app icons in the SelectLocalApps view Since we have the packageName, we can just fetch the Drawables directly. This uses some shortcuts to try to make things run faster. For example, the ImageView does not have an ID, instead it is references by the index number within the LinearLayout. --- res/layout/select_local_apps_list_item.xml | 56 +++++++++++++++++++ .../fragments/SelectLocalAppsFragment.java | 43 ++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 res/layout/select_local_apps_list_item.xml diff --git a/res/layout/select_local_apps_list_item.xml b/res/layout/select_local_apps_list_item.xml new file mode 100644 index 000000000..c29cd761b --- /dev/null +++ b/res/layout/select_local_apps_list_item.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java index 182d95dcb..87d692f9d 100644 --- a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java @@ -19,15 +19,19 @@ import android.app.ListFragment; import android.app.LoaderManager.LoaderCallbacks; import android.content.CursorLoader; import android.content.Loader; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.database.Cursor; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.view.ActionMode; import android.view.View; -import android.widget.ListView; +import android.widget.*; import android.widget.SearchView.OnQueryTextListener; -import android.widget.SimpleCursorAdapter; +import android.widget.SimpleCursorAdapter.ViewBinder; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.R; @@ -42,6 +46,8 @@ import java.util.HashSet; public class SelectLocalAppsFragment extends ListFragment implements LoaderCallbacks, OnQueryTextListener { + private PackageManager packageManager; + private Drawable defaultAppIcon; private SelectLocalAppsActivity selectLocalAppsActivity; private ActionMode mActionMode = null; private String mCurrentFilterString; @@ -57,21 +63,48 @@ public class SelectLocalAppsFragment extends ListFragment setEmptyText(getString(R.string.no_applications_found)); + packageManager = getActivity().getPackageManager(); + defaultAppIcon = getActivity().getResources() + .getDrawable(android.R.drawable.sym_def_app_icon); + selectLocalAppsActivity = (SelectLocalAppsActivity) getActivity(); ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); SimpleCursorAdapter adapter = new SimpleCursorAdapter(getActivity(), - android.R.layout.simple_list_item_activated_2, + R.layout.select_local_apps_list_item, null, new String[] { InstalledAppProvider.DataColumns.APPLICATION_LABEL, InstalledAppProvider.DataColumns.APP_ID, }, new int[] { - android.R.id.text1, - android.R.id.text2, + R.id.application_label, + R.id.package_name, }, 0); + adapter.setViewBinder(new ViewBinder() { + + @Override + public boolean setViewValue(View view, Cursor cursor, int columnIndex) { + Log.i("SelectLocalAppsFragment", "ViewBinder " + columnIndex); + if (columnIndex == cursor.getColumnIndex(InstalledAppProvider.DataColumns.APP_ID)) { + String packageName = cursor.getString(columnIndex); + TextView textView = (TextView) view.findViewById(R.id.package_name); + textView.setText(packageName); + LinearLayout ll = (LinearLayout) view.getParent().getParent(); + ImageView iconView = (ImageView) ll.getChildAt(0); + Drawable icon; + try { + icon = packageManager.getApplicationIcon(packageName); + } catch (NameNotFoundException e) { + icon = defaultAppIcon; + } + iconView.setImageDrawable(icon); + return true; + } + return false; + } + }); setListAdapter(adapter); setListShown(false); // start out with a progress indicator From 0a8d08aac87d5e6665aa39f16d9570085b5da8d2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 8 May 2014 01:12:41 -0400 Subject: [PATCH 14/14] ensure that "Update Repo" is always shown in the Action Mode Otherwise, it gets confusing what is the action the user should do. Perhaps the Action Mode "Done" button should always trigger the "Update Repo" action, right now it means do nothing and return. --- res/menu/select_local_apps_action_mode.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/menu/select_local_apps_action_mode.xml b/res/menu/select_local_apps_action_mode.xml index 89d8f8e7b..5c09264f4 100644 --- a/res/menu/select_local_apps_action_mode.xml +++ b/res/menu/select_local_apps_action_mode.xml @@ -8,7 +8,7 @@ \ No newline at end of file