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);