Merge branch 'swap_ux_improvements' of https://gitlab.com/eighthave/fdroidclient

This commit is contained in:
Daniel Martí 2014-05-08 12:53:04 +02:00
commit 295ef8f02a
20 changed files with 381 additions and 77 deletions

View File

@ -4,6 +4,7 @@
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="extern/nanohttpd/core/src/main/java"/>
<classpathentry kind="src" path="gen"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPinning"/>
<classpathentry combineaccessrules="false" kind="src" path="/MemorizingActivity"/>

View File

@ -4,23 +4,50 @@
android:layout_height="fill_parent"
android:orientation="vertical" >
<ToggleButton
<Button
android:id="@+id/enable_wifi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/enable_wifi" />
<CheckBox
android:id="@+id/repoSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:gravity="center"
android:padding="15dp"
android:text="@string/touch_to_turn_on_local_repo" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/sharing_uri" />
<TextView
android:id="@+id/sharing_uri"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="15dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:typeface="monospace" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/wifiNetwork"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/wifi_network" />
<TextView
android:id="@+id/wifiNetworkName"
android:id="@+id/wifi_network"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="15dp"

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/activatedBackgroundIndicator"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingBottom="2dip"
android:paddingTop="2dip"
tools:ignore="NewApi" >
<!-- TODO remove NewApi ignore when appcompat-v7 is in place -->
<ImageView
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
android:layout_marginTop="6dip"
tools:ignore="ContentDescription" />
<TwoLineListItem
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:mode="twoLine" >
<TextView
android:id="@+id/application_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
android:layout_marginTop="6dip"
android:textAppearance="?android:attr/textAppearanceListItem" />
<TextView
android:id="@+id/package_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/application_label"
android:layout_below="@+id/application_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
</TwoLineListItem>
</LinearLayout>

View File

@ -1,9 +1,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
android:title="@string/menu_search"/>
<item
android:id="@+id/action_update_repo"
android:icon="@android:drawable/ic_input_add"
android:showAsAction="ifRoom|withText"
android:showAsAction="always|withText"
android:title="@string/update_repo"/>
</menu>

View File

@ -1,10 +1,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_search"
android:actionViewClass="android.widget.SearchView"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="collapseActionView|ifRoom"
android:title="@string/menu_search"/>
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:icon="@android:drawable/ic_menu_preferences"
android:showAsAction="never"
android:title="@string/menu_preferences"/>
</menu>

View File

@ -156,6 +156,8 @@
<string name="local_repo_running">Your local FDroid repo is accessible.</string>
<string name="setup_repo">Setup Local Repo</string>
<string name="touch_to_configure_local_repo">Touch to setup your local repo.</string>
<string name="touch_to_turn_on_local_repo">Touch to turn on your local repo.</string>
<string name="touch_to_turn_off_local_repo">Touch to turn off your local repo.</string>
<string name="updating">Updating&#8230;</string>
<string name="update_repo">Update Repo</string>
<string name="deleting_repo">Deleting current repo&#8230;</string>
@ -168,6 +170,7 @@
<string name="icon">icon</string>
<string name="fingerprint">Fingerprint:</string>
<string name="wifi_network">WiFi Network:</string>
<string name="sharing_uri">Sharing URL:</string>
<string name="enable_wifi">Enable WiFi</string>
<string name="enabling_wifi">Enabling WiFi&#8230;</string>
<string name="same_wifi_instructions">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:</string>

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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<String, Void, Void> {
private static final String TAG = "QrGenAsyncTask";

View File

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

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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<Cursor> {
//TODO replace with appcompat-v7
@TargetApi(11)
public class SelectLocalAppsFragment extends ListFragment
implements LoaderCallbacks<Cursor>, 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<Cursor> 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;
}
}

View File

@ -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<T extends FDroidProvider> 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);