diff --git a/.classpath b/.classpath index d2ce7e889..fd733198e 100644 --- a/.classpath +++ b/.classpath @@ -4,6 +4,7 @@ + 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" /> + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/select_local_apps_action_mode.xml b/res/menu/select_local_apps_action_mode.xml index 2550ebcfe..5c09264f4 100644 --- a/res/menu/select_local_apps_action_mode.xml +++ b/res/menu/select_local_apps_action_mode.xml @@ -1,9 +1,14 @@ + \ No newline at end of file diff --git a/res/menu/select_local_apps_activity.xml b/res/menu/select_local_apps_activity.xml index 0d2d96089..d44df3029 100644 --- a/res/menu/select_local_apps_activity.xml +++ b/res/menu/select_local_apps_activity.xml @@ -1,10 +1,15 @@ + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index ae4f18a66..86fae6b4a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -156,6 +156,8 @@ 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/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; } } 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/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/data/DBHelper.java b/src/org/fdroid/fdroid/data/DBHelper.java index 4d81bb4b4..f0e8ab00d 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 = 46; private Context context; @@ -249,11 +250,11 @@ public class DBHelper extends SQLiteOpenHelper { addLastUpdatedToRepo(db, oldVersion); renameRepoId(db, oldVersion); populateRepoNames(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). */ @@ -398,6 +399,17 @@ public class DBHelper extends SQLiteOpenHelper { db.execSQL(CREATE_TABLE_INSTALLED_APP); } + private void addAppLabelToInstalledCache(SQLiteDatabase db, int oldVersion) { + if (oldVersion < 45) { + 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); + } + } + 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..aa584ffad 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,17 +54,24 @@ 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, + }; } 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); } @@ -71,6 +83,27 @@ 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; + 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; @@ -94,8 +127,16 @@ 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) { + sortOrder = DataColumns.APPLICATION_LABEL; + } + QuerySelection selection = new QuerySelection(customSelection, selectionArgs); switch (matcher.match(uri)) { case CODE_LIST: @@ -105,13 +146,17 @@ 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); 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; } 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 81b82fb2f..5e1ffc23a 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -19,21 +19,25 @@ 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.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"; private ProgressDialog repoProgress; private WifiManager wifiManager; - private ToggleButton repoSwitch; + private Button enableWifiButton; + private CheckBox repoSwitch; + + private Timer stopTimer; private int SET_IP_ADDRESS = 7345; private int UPDATE_REPO = 7346; @@ -42,9 +46,11 @@ 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); + enableWifiButton = (Button) findViewById(R.id.enable_wifi); + repoSwitch = (CheckBox) findViewById(R.id.repoSwitch); wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); } @@ -52,19 +58,35 @@ public class LocalRepoActivity extends Activity { public void onResume() { super.onResume(); resetNetworkInfo(); + 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() { @@ -74,19 +96,33 @@ 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) { 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 @@ -158,24 +194,34 @@ 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); } else { FDroidApp.stopLocalRepoService(LocalRepoActivity.this); + stopTimer.cancel(); // disable automatic stop } } }); } + 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)) 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. @@ -188,9 +234,10 @@ 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); + TextView wifiNetworkNameTextView = (TextView) findViewById(R.id.wifi_network); wifiNetworkNameTextView.setText(FDroidApp.ssid); TextView fingerprintTextView = (TextView) findViewById(R.id.fingerprint); diff --git a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java index a4889ec09..ffe90fae9 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; @@ -27,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); @@ -73,7 +71,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..6e933de92 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; @@ -33,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); @@ -92,12 +91,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()); } } diff --git a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java index ff0ad5445..71fc8ee9c 100644 --- a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java +++ b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java @@ -2,27 +2,28 @@ 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.FDroidApp; import org.fdroid.fdroid.PreferencesActivity; import org.fdroid.fdroid.R; 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) { super.onCreate(savedInstanceState); + ((FDroidApp) getApplication()).applyTheme(this); setContentView(R.layout.select_local_apps_activity); } @@ -30,13 +31,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 +50,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 +67,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 b9a6b7ce8..87d692f9d 100644 --- a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java @@ -15,17 +15,23 @@ 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.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.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.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.ViewBinder; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.R; @@ -35,39 +41,72 @@ 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 PackageManager packageManager; + private Drawable defaultAppIcon; 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); 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_1, + R.layout.select_local_apps_list_item, null, new String[] { + InstalledAppProvider.DataColumns.APPLICATION_LABEL, InstalledAppProvider.DataColumns.APP_ID, }, new int[] { - android.R.id.text1, + 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); + setListShown(false); // start out with a progress indicator // either reconnect with an existing loader or start a new one getLoaderManager().initLoader(0, null, this); @@ -84,15 +123,13 @@ 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) 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 { @@ -102,13 +139,19 @@ 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, - InstalledAppProvider.DataColumns.APP_ID); + InstalledAppProvider.DataColumns.APPLICATION_LABEL); return loader; } @@ -123,7 +166,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)) { @@ -144,4 +187,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; + } } 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);