Implemented client connection for swap.
Listen for a new intent, show a screen to the user mentioning they are about to start a swap. Make FDroid receive repo intents, then dispatch to relevant Activity. Previously manage repo always got the intents. Now FDroid does, and chooses whether to give to ManageRepos or Client connect. Not sure if it is required to do it this way or not, but it seems to work. I had a bit of an issue getting the "Welcome to F-Droid" string to fit on one line, because it was breaking on the hyphen. That is still not resolved in this commit. Still need to: * Show error messages instead of the "connect" description * Jar signing seems not to work when connecting to other repo. In order to handle returning to F-Droid after connecting (or saying no) I tagged the intent with a "handled" extra value. That way, I can ignore trying to connect to a repo if we've already handled that event. Finally, I also fixed an issue regarding downloading of signed index.jar files with an uppercase fingerprint. The fingerprint from the jar differed from that in the swap url, in that one was upper case and the other was lower case. This uses an .equalsIgnoreCase check instead. It also adds an extra guard in case the repo doesn't have a fingerprint. Although it may not even use the signed repo updater if both the pubkey and fingerprint are null, it is nice to have the extra assurance. Fixes issue #19. I also left some more TODO's around. I should put them in issues, but I'm in a bit of a hurry.
This commit is contained in:
parent
2a22a8b6cb
commit
80205e94fb
@ -109,6 +109,22 @@
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".SearchResults" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".views.swap.ConnectSwapActivity"
|
||||
android:theme="@style/SwapTheme.Wizard"
|
||||
android:label=""
|
||||
android:parentActivityName=".FDroid">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".FDroid" />
|
||||
<intent-filter>
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:host="*" />
|
||||
<data android:path="/FDROID/REPO/SWAP" />
|
||||
<data android:path="/fdroid/repo/swap" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!--
|
||||
The title for ManageReposActivity is "F-Droid" here, but "Repositories"
|
||||
when viewing the Activity itself in the app.
|
||||
@ -161,6 +177,30 @@
|
||||
<data android:path="/.*/FDROID/REPO" />
|
||||
<data android:path="/.*/.*/FDROID/REPO" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".SearchResults" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".views.swap.ConnectSwapActivity"
|
||||
android:theme="@style/SwapTheme.Wizard.ReceiveSwap"
|
||||
android:label=""
|
||||
android:noHistory="true"
|
||||
android:parentActivityName=".FDroid">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".FDroid" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ManageRepo"
|
||||
android:label="@string/menu_manage"
|
||||
android:launchMode="singleTask"
|
||||
android:parentActivityName=".FDroid" >
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".FDroid" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
23
res/drawable/swap_confirm_button_skin.xml
Normal file
23
res/drawable/swap_confirm_button_skin.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="@color/swap_confirm_pressed" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@color/swap_confirm" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
23
res/drawable/swap_deny_button_skin.xml
Normal file
23
res/drawable/swap_deny_button_skin.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="@color/swap_deny_pressed" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@color/swap_deny" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
70
res/layout/swap_confirm_receive.xml
Normal file
70
res/layout/swap_confirm_receive.xml
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="18dp">
|
||||
<!-- Padding is 32px * 0.56 = 18dip -->
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:src="@drawable/ic_launcher"
|
||||
android:contentDescription="@string/icon"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_width="117.6dp"
|
||||
android:layout_height="117.6dp"
|
||||
android:layout_centerHorizontal="true"/>
|
||||
<!-- 210 * 0.56 = 117.6 -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_title"
|
||||
android:text="@string/swap_welcome"
|
||||
style="@style/SwapTheme.Wizard.ReceiveSwap.MainText"
|
||||
android:layout_below="@id/icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="28sp"
|
||||
android:lines="1" />
|
||||
<!-- 60 * 0.56 = 33.6 -->
|
||||
<!-- Temporarily making it smaller than 33.6 until we figure out how to
|
||||
prevent line breaks on the hyphen in F-Droid. -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_description"
|
||||
android:text="@string/swap_confirm_connect"
|
||||
style="@style/SwapTheme.Wizard.Text"
|
||||
android:layout_below="@id/text_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="25.75sp"/>
|
||||
<!-- 46px * 0.56 = 25.76sp -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_below="@+id/text_description"
|
||||
android:layout_marginTop="45dp">
|
||||
<!-- 80px * 0.56 = 45dp -->
|
||||
|
||||
<Button
|
||||
android:id="@+id/no_button"
|
||||
android:text="@string/no"
|
||||
style="@style/SwapTheme.Wizard.ReceiveSwap.Deny"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/yes_button"
|
||||
android:text="@string/yes"
|
||||
style="@style/SwapTheme.Wizard.ReceiveSwap.Confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -31,13 +31,16 @@
|
||||
|
||||
<Button style="@style/SwapTheme.Wizard.OptionButton"
|
||||
android:text="@string/open_qr_code_scanner"
|
||||
android:layout_above="@id/btn_not_working" android:layout_gravity="center" android:id="@+id/button"/>
|
||||
android:layout_above="@id/btn_not_working"
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/button"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/device_ip_address"
|
||||
tools:text="192.168.1.1:8888"
|
||||
android:layout_above="@+id/button" android:layout_centerHorizontal="true"
|
||||
android:layout_above="@+id/button"
|
||||
android:layout_centerHorizontal="true"
|
||||
style="@style/SwapTheme.Wizard.LocalIpAddress"/>
|
||||
|
||||
</RelativeLayout>
|
@ -8,6 +8,10 @@
|
||||
<color name="swap_light_blue_pressed">#ff98cce1</color>
|
||||
<color name="swap_blue">#1c6bbc</color>
|
||||
<color name="swap_blue_pressed">#ff6ca8d5</color>
|
||||
<color name="swap_confirm">#ff27aae1</color>
|
||||
<color name="swap_confirm_pressed">#ff2ed7eb</color>
|
||||
<color name="swap_deny">#ff21488c</color>
|
||||
<color name="swap_deny_pressed">#ff3096b9</color>
|
||||
<color name="swap_wifi_may_work">#fbb040</color>
|
||||
<color name="swap_wifi_likely_to_work">#00a14b</color>
|
||||
|
||||
|
@ -305,4 +305,6 @@
|
||||
<string name="swap_view_available_networks">(Tap to open available networks)</string>
|
||||
<string name="swap_wifi_qr_not_working">It\'s not working</string>
|
||||
<string name="open_qr_code_scanner">Open QR Code Scanner</string>
|
||||
<string name="swap_welcome">Welcome to F-Droid!</string>
|
||||
<string name="swap_confirm_connect">Do you ant to get apps from %1$s now?</string>
|
||||
</resources>
|
||||
|
@ -109,19 +109,49 @@
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<!--
|
||||
Buttons down the bottom of the screen, which prompt the user for further
|
||||
info, or to change the process somehow (e.g. Use Bluetooth instead of Wifi).
|
||||
-->
|
||||
<style name="SwapTheme.Wizard.OptionButton">
|
||||
<style name="SwapTheme.Wizard.ButtonBase">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:padding">19dp</item> <!-- 34px * 96dpi / 160dpi -->
|
||||
<item name="android:textSize">20.25sp</item> <!-- 36px * 96dpi / 160dpi -->
|
||||
<item name="android:background">@drawable/swap_button_skin</item>
|
||||
<item name="android:textColor">#fff</item>
|
||||
<item name="android:fontFamily">sans-serif-light</item>
|
||||
</style>
|
||||
|
||||
<!--
|
||||
Buttons down the bottom of the screen, which prompt the user for further
|
||||
info, or to change the process somehow (e.g. Use Bluetooth instead of Wifi).
|
||||
-->
|
||||
<style name="SwapTheme.Wizard.OptionButton" parent="SwapTheme.Wizard.ButtonBase">
|
||||
<item name="android:background">@drawable/swap_button_skin</item>
|
||||
<item name="android:layout_margin">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="SwapTheme.Wizard.ReceiveSwap" parent="SwapTheme.Wizard">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<!--
|
||||
Buttons used to ask the user to confirm they want to receive a swap repo from someone
|
||||
-->
|
||||
<style name="SwapTheme.Wizard.ReceiveSwap.ButtonBase" parent="SwapTheme.Wizard.ButtonBase">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="SwapTheme.Wizard.ReceiveSwap.Confirm" parent="SwapTheme.Wizard.ReceiveSwap.ButtonBase">
|
||||
<item name="android:background">@drawable/swap_confirm_button_skin</item>
|
||||
<item name="android:layout_marginLeft">7dp</item>
|
||||
</style>
|
||||
|
||||
<style name="SwapTheme.Wizard.ReceiveSwap.Deny" parent="SwapTheme.Wizard.ReceiveSwap.ButtonBase">
|
||||
<item name="android:background">@drawable/swap_deny_button_skin</item>
|
||||
<item name="android:layout_marginRight">7dp</item>
|
||||
</style>
|
||||
|
||||
<style name="SwapTheme.Wizard.ReceiveSwap.MainText" parent="SwapTheme.Wizard.MainText">
|
||||
<item name="android:textSize">33.6sp</item> <!-- 60 * 96dpi / 160dpi -->
|
||||
<item name="android:textStyle">italic</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.app.NotificationManager;
|
||||
@ -41,12 +42,13 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.widget.Toast;
|
||||
import org.fdroid.fdroid.compat.TabManager;
|
||||
import org.fdroid.fdroid.data.AppProvider;
|
||||
import org.fdroid.fdroid.views.AppListFragmentPagerAdapter;
|
||||
import org.fdroid.fdroid.views.LocalRepoActivity;
|
||||
import org.fdroid.fdroid.views.ManageReposActivity;
|
||||
import org.fdroid.fdroid.views.swap.ConnectSwapActivity;
|
||||
import org.fdroid.fdroid.views.swap.SwapActivity;
|
||||
|
||||
public class FDroid extends ActionBarActivity {
|
||||
@ -55,9 +57,12 @@ public class FDroid extends ActionBarActivity {
|
||||
public static final int REQUEST_MANAGEREPOS = 1;
|
||||
public static final int REQUEST_PREFS = 2;
|
||||
public static final int REQUEST_ENABLE_BLUETOOTH = 3;
|
||||
public static final int REQUEST_SWAP = 4;
|
||||
|
||||
public static final String EXTRA_TAB_UPDATE = "extraTab";
|
||||
|
||||
public static final String ACTION_ADD_REPO = "org.fdroid.fdroid.FDroid.ACTION_ADD_REPO";
|
||||
|
||||
private FDroidApp fdroidApp = null;
|
||||
|
||||
private ViewPager viewPager;
|
||||
@ -108,6 +113,25 @@ public class FDroid extends ActionBarActivity {
|
||||
super.onResume();
|
||||
// AppDetails and RepoDetailsActivity set different NFC actions, so reset here
|
||||
NfcBeamManager.setAndroidBeam(this, getApplication().getPackageName());
|
||||
checkForAddRepoIntent();
|
||||
}
|
||||
|
||||
private void checkForAddRepoIntent() {
|
||||
// Don't handle the intent after coming back to this view (e.g. after hitting the back button)
|
||||
// http://stackoverflow.com/a/14820849
|
||||
if (!getIntent().hasExtra("handled")) {
|
||||
RepoIntentParser parser = new RepoIntentParser(this, getIntent());
|
||||
if (parser.parse()) {
|
||||
getIntent().putExtra("handled", true);
|
||||
if (parser.isFromSwap()) {
|
||||
startActivityForResult(new Intent(ACTION_ADD_REPO, getIntent().getData(), this, ConnectSwapActivity.class), REQUEST_SWAP);
|
||||
} else {
|
||||
startActivity(new Intent(ACTION_ADD_REPO, getIntent().getData(), this, ManageRepo.class));
|
||||
}
|
||||
} else if (parser.getErrorMessage() != null) {
|
||||
Toast.makeText(this, parser.getErrorMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
148
src/org/fdroid/fdroid/RepoIntentParser.java
Normal file
148
src/org/fdroid/fdroid/RepoIntentParser.java
Normal file
@ -0,0 +1,148 @@
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class RepoIntentParser {
|
||||
|
||||
private final Intent intent;
|
||||
private final Context context;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
private String repoUriString;
|
||||
private Uri repoUri;
|
||||
private String host;
|
||||
private int port;
|
||||
private String scheme;
|
||||
private String fingerprint;
|
||||
private String bssid = null;
|
||||
private String ssid = null;
|
||||
private boolean fromSwap;
|
||||
|
||||
public RepoIntentParser(Context context, Intent intent) {
|
||||
this.intent = intent;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public String getBssid() {
|
||||
return bssid;
|
||||
}
|
||||
|
||||
public String getSsid() {
|
||||
return ssid;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return repoUriString;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return scheme;
|
||||
}
|
||||
|
||||
public String getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This has changed a little from before, it used to be: port != 8888 and !ip then not local repo.
|
||||
* TODO: Port no longer fixed to 8888, as we can use whatever port we want from the settings of the local
|
||||
* repo device. However, perhaps we should choose a higher port, as a lot of regular web servers may opt for 8888
|
||||
* in liu of root permissions on the server for using port 80, and if port 8080 is already taken.
|
||||
*/
|
||||
public boolean looksLikeLocalAddress() {
|
||||
return port == 8888 && host.matches("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+");
|
||||
}
|
||||
|
||||
public boolean parse() {
|
||||
|
||||
/* an URL from a click, NFC, QRCode scan, etc */
|
||||
repoUri = intent.getData();
|
||||
if (repoUri == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.d("org.fdroid.fdroid.RepoIntentParser", "Parsing intent URI " + repoUri);
|
||||
|
||||
// scheme and host should only ever be pure ASCII aka Locale.ENGLISH
|
||||
scheme = intent.getScheme();
|
||||
host = repoUri.getHost();
|
||||
port = repoUri.getPort();
|
||||
if (scheme == null || host == null) {
|
||||
this.errorMessage = String.format(context.getString(R.string.malformed_repo_uri), repoUri);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (equalsInList(scheme, new String[]{"FDROIDREPO", "FDROIDREPOS"})) {
|
||||
/*
|
||||
* QRCodes are more efficient in all upper case, so QR URIs are
|
||||
* encoded in all upper case, then forced to lower case.
|
||||
* Checking if the special F-Droid scheme being all is upper
|
||||
* case means it should be downcased.
|
||||
*/
|
||||
repoUri = Uri.parse(repoUri.toString().toLowerCase(Locale.ENGLISH));
|
||||
} else if (repoUri.getPath().startsWith("/FDROID/REPO")) {
|
||||
/*
|
||||
* some QR scanners chop off the fdroidrepo:// and just try
|
||||
* http://, then the incoming URI does not get downcased
|
||||
* properly, and the query string is stripped off. So just
|
||||
* downcase the path, and carry on to get something working.
|
||||
*/
|
||||
repoUri = Uri.parse(repoUri.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
// make scheme and host lowercase so they're readable in dialogs
|
||||
scheme = scheme.toLowerCase(Locale.ENGLISH);
|
||||
host = host.toLowerCase(Locale.ENGLISH);
|
||||
fingerprint = repoUri.getQueryParameter("fingerprint");
|
||||
bssid = repoUri.getQueryParameter("bssid");
|
||||
ssid = repoUri.getQueryParameter("ssid");
|
||||
fromSwap = repoUri.getQueryParameter("swap") != null;
|
||||
|
||||
Log.i("RepoListFragment", "onCreate " + fingerprint);
|
||||
if (equalsInList(scheme, new String[] { "fdroidrepos", "fdroidrepo", "https", "http" })) {
|
||||
|
||||
/* sanitize and format for function and readability */
|
||||
repoUriString = repoUri.toString()
|
||||
.replaceAll("\\?.*$", "") // remove the whole query
|
||||
.replaceAll("/*$", "") // remove all trailing slashes
|
||||
.replace(repoUri.getHost(), host) // downcase host name
|
||||
.replace(intent.getScheme(), scheme) // downcase scheme
|
||||
.replace("fdroidrepo", "http"); // proper repo address
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean equalsInList(String value, String[] list) {
|
||||
for (String item : list) {
|
||||
if (item.equals(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFromSwap() {
|
||||
return fromSwap;
|
||||
}
|
||||
}
|
@ -28,10 +28,6 @@ import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import com.nostra13.universalimageloader.utils.StorageUtils;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.xml.sax.XMLReader;
|
||||
@ -297,6 +293,7 @@ public final class Utils {
|
||||
return Uri.parse("http://wifi-not-enabled");
|
||||
Uri uri = Uri.parse(repo.address.replaceFirst("http", "fdroidrepo"));
|
||||
Uri.Builder b = uri.buildUpon();
|
||||
b.appendQueryParameter("swap", "1");
|
||||
if (!TextUtils.isEmpty(repo.fingerprint))
|
||||
b.appendQueryParameter("fingerprint", repo.fingerprint);
|
||||
if (!TextUtils.isEmpty(FDroidApp.bssid)) {
|
||||
|
@ -20,6 +20,12 @@ public class RepoProvider extends FDroidProvider {
|
||||
|
||||
private Helper() {}
|
||||
|
||||
public static Repo findByUri(Context context, Uri uri) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Cursor cursor = resolver.query(uri, DataColumns.ALL, null, null, null);
|
||||
return cursorToRepo(cursor);
|
||||
}
|
||||
|
||||
public static Repo findById(Context context, long repoId) {
|
||||
return findById(context, repoId, DataColumns.ALL);
|
||||
}
|
||||
@ -29,15 +35,7 @@ public class RepoProvider extends FDroidProvider {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Uri uri = RepoProvider.getContentUri(repoId);
|
||||
Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
Repo repo = null;
|
||||
if (cursor != null) {
|
||||
if (cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
repo = new Repo(cursor);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return repo;
|
||||
return cursorToRepo(cursor);
|
||||
}
|
||||
|
||||
public static Repo findByAddress(Context context, String address) {
|
||||
@ -90,6 +88,18 @@ public class RepoProvider extends FDroidProvider {
|
||||
return repos;
|
||||
}
|
||||
|
||||
private static Repo cursorToRepo(Cursor cursor) {
|
||||
Repo repo = null;
|
||||
if (cursor != null) {
|
||||
if (cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
repo = new Repo(cursor);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return repo;
|
||||
}
|
||||
|
||||
public static void update(Context context, Repo repo,
|
||||
ContentValues values) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
@ -152,11 +162,11 @@ public class RepoProvider extends FDroidProvider {
|
||||
* resolver, but I thought I'd put it here in the interests of having
|
||||
* each of the CRUD methods available in the helper class.
|
||||
*/
|
||||
public static void insert(Context context,
|
||||
public static Uri insert(Context context,
|
||||
ContentValues values) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Uri uri = RepoProvider.getContentUri();
|
||||
resolver.insert(uri, values);
|
||||
return resolver.insert(uri, values);
|
||||
}
|
||||
|
||||
public static void remove(Context context, long repoId) {
|
||||
|
@ -130,7 +130,7 @@ public class LocalRepoManager {
|
||||
this.uriString = uriString;
|
||||
}
|
||||
|
||||
private String writeFdroidApkToWebroot(String repoAddress) {
|
||||
private String writeFdroidApkToWebroot() {
|
||||
ApplicationInfo appInfo;
|
||||
String fdroidClientURL = "https://f-droid.org/FDroid.apk";
|
||||
|
||||
@ -148,7 +148,7 @@ public class LocalRepoManager {
|
||||
}
|
||||
|
||||
public void writeIndexPage(String repoAddress) {
|
||||
final String fdroidClientURL = writeFdroidApkToWebroot(repoAddress);
|
||||
final String fdroidClientURL = writeFdroidApkToWebroot();
|
||||
try {
|
||||
File indexHtml = new File(webRoot, "index.html");
|
||||
BufferedReader in = new BufferedReader(
|
||||
|
@ -15,6 +15,8 @@ import java.util.jar.JarFile;
|
||||
|
||||
public class SignedRepoUpdater extends RepoUpdater {
|
||||
|
||||
private static final String TAG = "org.fdroid.fdroid.updater.SignedRepoUpdater";
|
||||
|
||||
public SignedRepoUpdater(Context ctx, Repo repo) {
|
||||
super(ctx, repo);
|
||||
}
|
||||
@ -25,15 +27,22 @@ public class SignedRepoUpdater extends RepoUpdater {
|
||||
throw new UpdateException(repo, "No signature found in index");
|
||||
}
|
||||
|
||||
Log.d("FDroid", "Index has " + certs.length + " signature(s)");
|
||||
Log.d(TAG, "Index has " + certs.length + " signature(s)");
|
||||
boolean match = false;
|
||||
for (Certificate cert : certs) {
|
||||
String certdata = Hasher.hex(cert);
|
||||
if (repo.pubkey == null && repo.fingerprint.equals(Utils.calcFingerprint(cert))) {
|
||||
repo.pubkey = certdata;
|
||||
usePubkeyInJar = true;
|
||||
if (repo.pubkey == null && repo.fingerprint != null) {
|
||||
String certFingerprint = Utils.calcFingerprint(cert);
|
||||
Log.d(TAG, "No public key for repo " + repo.address + " yet, but it does have a fingerprint, so comparing them.");
|
||||
Log.d(TAG, "Repo fingerprint: " + repo.fingerprint);
|
||||
Log.d(TAG, "Cert fingerprint: " + certFingerprint);
|
||||
if (repo.fingerprint.equalsIgnoreCase(certFingerprint)) {
|
||||
repo.pubkey = certdata;
|
||||
usePubkeyInJar = true;
|
||||
}
|
||||
}
|
||||
if (repo.pubkey != null && repo.pubkey.equals(certdata)) {
|
||||
Log.d(TAG, "Checking repo public key against cert found in jar.");
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
@ -105,7 +114,7 @@ public class SignedRepoUpdater extends RepoUpdater {
|
||||
protected File getIndexFromFile(File downloadedFile) throws
|
||||
UpdateException {
|
||||
Date updateTime = new Date(System.currentTimeMillis());
|
||||
Log.d("FDroid", "Getting signed index from " + repo.address + " at " +
|
||||
Log.d(TAG, "Getting signed index from " + repo.address + " at " +
|
||||
Utils.LOG_DATE_FORMAT.format(updateTime));
|
||||
|
||||
File indexJar = downloadedFile;
|
||||
|
106
src/org/fdroid/fdroid/views/swap/ConfirmReceiveSwapFragment.java
Normal file
106
src/org/fdroid/fdroid/views/swap/ConfirmReceiveSwapFragment.java
Normal file
@ -0,0 +1,106 @@
|
||||
package org.fdroid.fdroid.views.swap;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import org.fdroid.fdroid.ProgressListener;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.RepoIntentParser;
|
||||
import org.fdroid.fdroid.UpdateService;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
|
||||
public class ConfirmReceiveSwapFragment extends Fragment implements ProgressListener {
|
||||
|
||||
private RepoIntentParser parser;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
|
||||
View view = inflater.inflate(R.layout.swap_confirm_receive, container, false);
|
||||
|
||||
view.findViewById(R.id.no_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
view.findViewById(R.id.yes_button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
confirm();
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
parser = new RepoIntentParser(getActivity(), getActivity().getIntent());
|
||||
if (parser.parse()) {
|
||||
((TextView) getView().findViewById(R.id.text_description)).setText(
|
||||
getString(R.string.swap_confirm_connect, parser.getHost())
|
||||
);
|
||||
} else {
|
||||
// TODO: Show error message on screen (not in popup).
|
||||
}
|
||||
}
|
||||
|
||||
private void confirm() {
|
||||
Repo repo = ensureRepoExists();
|
||||
UpdateService.updateRepoNow(repo.address, getActivity()).setListener(this);
|
||||
}
|
||||
|
||||
private Repo ensureRepoExists() {
|
||||
// TODO: parser.getUri() will include a fingerprint, which may not match with
|
||||
// the repos address in the database.
|
||||
Repo repo = RepoProvider.Helper.findByAddress(getActivity(), parser.getUri());
|
||||
if (repo == null) {
|
||||
ContentValues values = new ContentValues(5);
|
||||
|
||||
// TODO: i18n and think about most appropriate name. Although ideally, it will not be seen often,
|
||||
// because we're whacking a pretty UI over the swap process so they don't need to "Manage repos"...
|
||||
values.put(RepoProvider.DataColumns.NAME, "Swap");
|
||||
values.put(RepoProvider.DataColumns.ADDRESS, parser.getUri());
|
||||
values.put(RepoProvider.DataColumns.DESCRIPTION, ""); // TODO;
|
||||
values.put(RepoProvider.DataColumns.FINGERPRINT, parser.getFingerprint());
|
||||
values.put(RepoProvider.DataColumns.IN_USE, true);
|
||||
Uri uri = RepoProvider.Helper.insert(getActivity(), values);
|
||||
repo = RepoProvider.Helper.findByUri(getActivity(), uri);
|
||||
}
|
||||
return repo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(Event event) {
|
||||
// TODO: Show progress, but we can worry about that later.
|
||||
// Might be nice to have it nicely embedded in the UI, rather than as
|
||||
// an additional dialog. E.g. White text on blue, letting the user
|
||||
// know what we are up to.
|
||||
|
||||
if (event.type.equals(UpdateService.EVENT_COMPLETE_AND_SAME) ||
|
||||
event.type.equals(UpdateService.EVENT_COMPLETE_WITH_CHANGES)) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("category", parser.getHost()); // TODO: Load repo from database to get proper name. This is what the category we want to select will be called.
|
||||
getActivity().setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
} else if (event.type.equals(UpdateService.EVENT_ERROR)) {
|
||||
// TODO: Show message on this screen (with a big "okay" button that goes back to F-Droid activity)
|
||||
// rather than finishing directly.
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
41
src/org/fdroid/fdroid/views/swap/ConnectSwapActivity.java
Normal file
41
src/org/fdroid/fdroid/views/swap/ConnectSwapActivity.java
Normal file
@ -0,0 +1,41 @@
|
||||
package org.fdroid.fdroid.views.swap;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
||||
public class ConnectSwapActivity extends FragmentActivity {
|
||||
|
||||
private static final String STATE_CONFIRM = "startSwap";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(android.R.id.content, new ConfirmReceiveSwapFragment(), STATE_CONFIRM)
|
||||
.addToBackStack(STATE_CONFIRM)
|
||||
.commit();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (currentState().equals(STATE_CONFIRM)) {
|
||||
finish();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private String currentState() {
|
||||
FragmentManager.BackStackEntry lastFragment = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
|
||||
return lastFragment.getName();
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.LightingColorFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
@ -15,9 +16,11 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.QrGenAsyncTask;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
@ -42,6 +45,16 @@ public class WifiQrFragment extends Fragment {
|
||||
// Replace all blacks with the background blue.
|
||||
qrImage.setColorFilter(new LightingColorFilter(0xffffffff, getResources().getColor(R.color.swap_blue)));
|
||||
|
||||
Button openQr = (Button)view.findViewById(R.id.button);
|
||||
openQr.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// TODO: What is the "correct" intent for starting barcode scanner?
|
||||
// I realise that google stuffed up by not standardising this, so
|
||||
// not quite sure the best way to proceed.
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@ -59,8 +72,10 @@ public class WifiQrFragment extends Fragment {
|
||||
if (TextUtils.isEmpty(FDroidApp.repo.address))
|
||||
return;
|
||||
|
||||
String scheme = Preferences.get().isLocalRepoHttpsEnabled() ? "https://" : "http://";
|
||||
|
||||
// the fingerprint is not useful on the button label
|
||||
String buttonLabel = FDroidApp.ipAddressString + ":" + FDroidApp.port;
|
||||
String buttonLabel = scheme + FDroidApp.ipAddressString + ":" + FDroidApp.port;
|
||||
TextView ipAddressView = (TextView) getView().findViewById(R.id.device_ip_address);
|
||||
ipAddressView.setText(buttonLabel);
|
||||
|
||||
@ -71,10 +86,25 @@ public class WifiQrFragment extends Fragment {
|
||||
* wifi AP to join. Lots of QR Scanners are buggy and do not respect
|
||||
* custom URI schemes, so we have to use http:// or https:// :-(
|
||||
*/
|
||||
final String qrUriString = Utils.getSharingUri(getActivity(), FDroidApp.repo).toString()
|
||||
.replaceFirst("fdroidrepo", "http")
|
||||
.replaceAll("ssid=[^?]*", "")
|
||||
.toUpperCase(Locale.ENGLISH);
|
||||
Uri sharingUri = Utils.getSharingUri(getActivity(), FDroidApp.repo);
|
||||
String qrUriString = ( scheme + sharingUri.getHost() ).toUpperCase(Locale.ENGLISH);
|
||||
if (sharingUri.getPort() != 80) {
|
||||
qrUriString += ":" + sharingUri.getPort();
|
||||
}
|
||||
qrUriString += sharingUri.getPath().toUpperCase(Locale.ENGLISH);
|
||||
boolean first = true;
|
||||
for (String parameterName : sharingUri.getQueryParameterNames()) {
|
||||
if (!parameterName.equals("ssid")) {
|
||||
if (first) {
|
||||
qrUriString += "?";
|
||||
first = false;
|
||||
} else {
|
||||
qrUriString += "&";
|
||||
}
|
||||
qrUriString += parameterName.toUpperCase(Locale.ENGLISH) + "=" +
|
||||
sharingUri.getQueryParameter(parameterName).toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i("QRURI", qrUriString);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user