From 52e0f373af74369b6de416936d087cf0ac8cdb3c Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 8 Apr 2014 14:32:50 -0400 Subject: [PATCH 1/3] stay in FDroid after adding a new repo via Intent If a new repo comes in via Intent, like from clicking a link, scanning a QR Code, etc., then stay in FDroid once the add dialog is complete. Previously, it would sometimes stay in FDroid and sometimes go back to the sending Activity, depending on the sending Activity. It was confusing and annoying behavior. --- AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ae0be10db..75c1ce9ec 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -96,9 +96,8 @@ Date: Sun, 20 Apr 2014 21:58:08 -0400 Subject: [PATCH 2/3] enable sending installed APKs via NFC/Android Beam on AppDetails If you are viewing the AppDetails screen for an installed app, this code configures Android Beam to send the APK for that installed app if the you initiate via NFC. Also move the SDK checks into each method so that they are easier to use without doing the wrong thing. --- src/org/fdroid/fdroid/AppDetails.java | 12 ++++-- src/org/fdroid/fdroid/FDroid.java | 24 +---------- src/org/fdroid/fdroid/NfcBeamManager.java | 43 +++++++++++++++++++ .../fdroid/views/RepoDetailsActivity.java | 13 +++--- 4 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 src/org/fdroid/fdroid/NfcBeamManager.java diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index d4d570471..51e7fdecb 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -34,11 +34,13 @@ import android.app.AlertDialog; import android.app.ListActivity; import android.app.ProgressDialog; import android.net.Uri; +import android.nfc.NfcAdapter; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.content.pm.Signature; @@ -624,11 +626,14 @@ public class AppDetails extends ListActivity { adapter.notifyDataSetChanged(); TextView tv = (TextView) findViewById(R.id.status); - if (!app.isInstalled()) - tv.setText(getString(R.string.details_notinstalled)); - else + if (app.isInstalled()) { tv.setText(getString(R.string.details_installed, app.installedVersionName)); + NfcBeamManager.setAndroidBeam(this, app.id); + } else { + tv.setText(getString(R.string.details_notinstalled)); + NfcBeamManager.disableAndroidBeam(this); + } tv = (TextView) infoView.findViewById(R.id.signature); if (pref_expert && mInstalledSignature != null) { @@ -1101,5 +1106,4 @@ public class AppDetails extends ListActivity { break; } } - } diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index 8dc53721d..f4e912fa2 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -129,9 +129,8 @@ public class FDroid extends FragmentActivity { @Override protected void onResume() { super.onResume(); - // RepoDetailsActivity sets a different beam, so reset here - if (Build.VERSION.SDK_INT >= 16) - setupAndroidBeam(); + // AppDetails and RepoDetailsActivity set different NFC actions, so reset here + NfcBeamManager.setAndroidBeam(this, getApplication().getPackageName()); } @Override @@ -422,23 +421,4 @@ public class FDroid extends FragmentActivity { } - @TargetApi(16) - private void setupAndroidBeam() { - PackageManager pm = getPackageManager(); - NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); - if (nfcAdapter != null) { - ApplicationInfo appInfo; - try { - appInfo = pm.getApplicationInfo("org.fdroid.fdroid", - PackageManager.GET_META_DATA); - // TODO can we send the repo here also, as a file? - Uri uris[] = { - Uri.parse("file://" + appInfo.publicSourceDir), - }; - nfcAdapter.setBeamPushUris(uris, this); - } catch (NameNotFoundException e1) { - e1.printStackTrace(); - } - } - } } diff --git a/src/org/fdroid/fdroid/NfcBeamManager.java b/src/org/fdroid/fdroid/NfcBeamManager.java new file mode 100644 index 000000000..488fe7d67 --- /dev/null +++ b/src/org/fdroid/fdroid/NfcBeamManager.java @@ -0,0 +1,43 @@ + +package org.fdroid.fdroid; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.nfc.NfcAdapter; +import android.os.Build; + +@TargetApi(16) +public class NfcBeamManager { + + static void setAndroidBeam(Activity activity, String packageName) { + if (Build.VERSION.SDK_INT < 16) + return; + PackageManager pm = activity.getPackageManager(); + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity); + if (nfcAdapter != null) { + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + Uri uris[] = { + Uri.parse("file://" + appInfo.publicSourceDir), + }; + nfcAdapter.setBeamPushUris(uris, activity); + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + } + } + + static void disableAndroidBeam(Activity activity) { + if (Build.VERSION.SDK_INT < 16) + return; + NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity); + if (nfcAdapter != null) + nfcAdapter.setBeamPushUris(null, activity); + } + +} diff --git a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java index 268557372..822975b01 100644 --- a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -69,14 +69,12 @@ public class RepoDetailsActivity extends FragmentActivity { setTitle(repo.getName()); wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); - - // required NFC support starts in android-14 - if (Build.VERSION.SDK_INT >= 14) - setNfc(); } @TargetApi(14) private void setNfc() { + if (Build.VERSION.SDK_INT < 14) + return; NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) { return; @@ -97,8 +95,9 @@ public class RepoDetailsActivity extends FragmentActivity { public void onResume() { Log.i(TAG, "onResume"); super.onResume(); - if (Build.VERSION.SDK_INT >= 9) - processIntent(getIntent()); + // FDroid.java and AppDetails set different NFC actions, so reset here + setNfc(); + processIntent(getIntent()); } @Override @@ -112,6 +111,8 @@ public class RepoDetailsActivity extends FragmentActivity { @TargetApi(9) void processIntent(Intent i) { + if (Build.VERSION.SDK_INT < 9) + return; if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(i.getAction())) { Log.i(TAG, "ACTION_NDEF_DISCOVERED"); Parcelable[] rawMsgs = From 4a55cdf9380fc77b51621e53690b53a63036ff09 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 21 Apr 2014 21:13:34 -0400 Subject: [PATCH 3/3] option to send via bluetooth any installed app on the AppDetails page This takes the code used for sending the FDroid.apk and applies it to any installed app. So the user can go to the AppDetails for any installed app and select "Send via Bluetooth" from the menu, and send the app to another phone. --- res/values/strings.xml | 1 + src/org/fdroid/fdroid/AppDetails.java | 49 +++++++++++++------ src/org/fdroid/fdroid/FDroid.java | 64 ++----------------------- src/org/fdroid/fdroid/FDroidApp.java | 68 +++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 73 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 0d0861e7c..b068d0ae9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -78,6 +78,7 @@ Go to NFC Settings… No Bluetooth send method found, choose one! Choose Bluetooth send method + Send via Bluetooth Repository address Fingerprint (optional) diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 51e7fdecb..1e40c9ba1 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -19,11 +19,6 @@ package org.fdroid.fdroid; -import java.io.File; -import java.security.NoSuchAlgorithmException; -import java.util.Iterator; -import java.util.List; - import android.content.*; import android.widget.*; import org.fdroid.fdroid.data.*; @@ -33,6 +28,7 @@ import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.ListActivity; import android.app.ProgressDialog; +import android.bluetooth.BluetoothAdapter; import android.net.Uri; import android.nfc.NfcAdapter; import android.os.Build; @@ -40,6 +36,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; +import android.support.v4.app.NavUtils; +import android.support.v4.view.MenuItemCompat; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; @@ -60,26 +58,31 @@ import android.view.View; import android.view.ViewGroup; import android.graphics.Bitmap; -import android.support.v4.app.NavUtils; -import android.support.v4.view.MenuItemCompat; - -import org.fdroid.fdroid.compat.PackageManagerCompat; -import org.fdroid.fdroid.compat.ActionBarCompat; -import org.fdroid.fdroid.compat.MenuManager; -import org.fdroid.fdroid.Utils.CommaSeparatedList; - import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.ImageScaleType; +import org.fdroid.fdroid.Utils.CommaSeparatedList; +import org.fdroid.fdroid.compat.ActionBarCompat; +import org.fdroid.fdroid.compat.MenuManager; +import org.fdroid.fdroid.compat.PackageManagerCompat; + +import java.io.File; +import java.security.NoSuchAlgorithmException; +import java.util.Iterator; +import java.util.List; + public class AppDetails extends ListActivity { + private static final String TAG = "AppDetails"; private static final int REQUEST_INSTALL = 0; private static final int REQUEST_UNINSTALL = 1; + public static final int REQUEST_ENABLE_BLUETOOTH = 2; public static final String EXTRA_APPID = "appid"; public static final String EXTRA_FROM = "from"; + private FDroidApp fdroidApp; private ApkListAdapter adapter; private static class ViewHolder { @@ -236,6 +239,7 @@ public class AppDetails extends ListActivity { private static final int DOGECOIN = Menu.FIRST + 12; private static final int FLATTR = Menu.FIRST + 13; private static final int DONATE_URL = Menu.FIRST + 14; + private static final int SEND_VIA_BLUETOOTH = Menu.FIRST + 15; private App app; private String appid; @@ -255,7 +259,8 @@ public class AppDetails extends ListActivity { @Override protected void onCreate(Bundle savedInstanceState) { - ((FDroidApp) getApplication()).applyTheme(this); + fdroidApp = ((FDroidApp) getApplication()); + fdroidApp.applyTheme(this); super.onCreate(savedInstanceState); @@ -760,6 +765,9 @@ public class AppDetails extends ListActivity { if (app.donateURL != null) donate.add(Menu.NONE, DONATE_URL, 10, R.string.menu_website); } + if (app.isInstalled() && fdroidApp.bluetoothAdapter != null) { // ignore on devices without Bluetooth + menu.add(Menu.NONE, SEND_VIA_BLUETOOTH, 6, R.string.send_via_bluetooth); + } return true; } @@ -850,6 +858,17 @@ public class AppDetails extends ListActivity { tryOpenUri(app.donateURL); return true; + case SEND_VIA_BLUETOOTH: + /* + * If Bluetooth has not been enabled/turned on, then + * enabling device discoverability will automatically enable Bluetooth + */ + Intent discoverBt = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); + discoverBt.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 121); + startActivityForResult(discoverBt, REQUEST_ENABLE_BLUETOOTH); + // if this is successful, the Bluetooth transfer is started + return true; + } return super.onOptionsItemSelected(item); } @@ -1104,6 +1123,8 @@ public class AppDetails extends ListActivity { case REQUEST_UNINSTALL: resetRequired = true; break; + case REQUEST_ENABLE_BLUETOOTH: + fdroidApp.sendViaBluetooth(this, resultCode, app.id); } } } diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index f4e912fa2..48b87e76d 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -20,18 +20,15 @@ package org.fdroid.fdroid; import android.annotation.TargetApi; -import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.NotificationManager; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; import android.content.*; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.database.ContentObserver; import android.net.Uri; @@ -48,7 +45,6 @@ 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; @@ -70,8 +66,7 @@ public class FDroid extends FragmentActivity { private static final int SEARCH = Menu.FIRST + 4; private static final int BLUETOOTH_APK = Menu.FIRST + 5; - /* request codes for Bluetooth flows */ - private BluetoothAdapter mBluetoothAdapter = null; + private FDroidApp fdroidApp = null; private ViewPager viewPager; @@ -80,7 +75,8 @@ public class FDroid extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { - ((FDroidApp) getApplication()).applyTheme(this); + fdroidApp = ((FDroidApp) getApplication()); + fdroidApp.applyTheme(this); super.onCreate(savedInstanceState); setContentView(R.layout.fdroid); @@ -112,18 +108,6 @@ public class FDroid extends FragmentActivity { Uri uri = AppProvider.getContentUri(); getContentResolver().registerContentObserver(uri, true, new AppObserver()); - - getBluetoothAdapter(); - } - - @TargetApi(18) - private void getBluetoothAdapter() { - // to use the new, recommended way of getting the adapter - // http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html - if (Build.VERSION.SDK_INT < 18) - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - else - mBluetoothAdapter = ((BluetoothManager) getSystemService(BLUETOOTH_SERVICE)).getAdapter(); } @Override @@ -149,7 +133,7 @@ public class FDroid extends FragmentActivity { android.R.drawable.ic_menu_agenda); MenuItem search = menu.add(Menu.NONE, SEARCH, 3, R.string.menu_search).setIcon( android.R.drawable.ic_menu_search); - if (mBluetoothAdapter != null) // ignore on devices without Bluetooth + if (fdroidApp.bluetoothAdapter != null) // ignore on devices without Bluetooth menu.add(Menu.NONE, BLUETOOTH_APK, 3, R.string.menu_send_apk_bt); menu.add(Menu.NONE, PREFERENCES, 4, R.string.menu_preferences).setIcon( android.R.drawable.ic_menu_preferences); @@ -298,45 +282,7 @@ public class FDroid extends FragmentActivity { } break; case REQUEST_ENABLE_BLUETOOTH: - if (resultCode == Activity.RESULT_CANCELED) - break; - String packageName = null; - String className = null; - boolean found = false; - Intent sendBt = null; - try { - PackageManager pm = getPackageManager(); - ApplicationInfo appInfo = pm.getApplicationInfo("org.fdroid.fdroid", - PackageManager.GET_META_DATA); - sendBt = new Intent(Intent.ACTION_SEND); - // The APK type is blocked by stock Android, so use zip - // sendBt.setType("application/vnd.android.package-archive"); - sendBt.setType("application/zip"); - sendBt.putExtra(Intent.EXTRA_STREAM, - Uri.parse("file://" + appInfo.publicSourceDir)); - // not all devices have the same Bluetooth Activities, so - // let's find it - for (ResolveInfo info : pm.queryIntentActivities(sendBt, 0)) { - packageName = info.activityInfo.packageName; - if (packageName.equals("com.android.bluetooth") - || packageName.equals("com.mediatek.bluetooth")) { - className = info.activityInfo.name; - found = true; - break; - } - } - } catch (NameNotFoundException e1) { - e1.printStackTrace(); - found = false; - } - if (!found) { - Toast.makeText(this, R.string.bluetooth_activity_not_found, - Toast.LENGTH_SHORT).show(); - startActivity(Intent.createChooser(sendBt, getString(R.string.choose_bt_send))); - } else { - sendBt.setClassName(packageName, className); - startActivity(sendBt); - } + fdroidApp.sendViaBluetooth(this, resultCode, "org.fdroid.fdroid"); break; } } diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 4ec114050..b722a1221 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -18,17 +18,31 @@ package org.fdroid.fdroid; +import android.annotation.TargetApi; import android.app.Activity; import android.app.Application; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; +import android.widget.Toast; + import com.nostra13.universalimageloader.cache.disc.impl.LimitedAgeDiscCache; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.utils.StorageUtils; + import de.duenndns.ssl.MemorizingTrustManager; + import org.fdroid.fdroid.compat.PRNGFixes; import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.InstalledAppCacheUpdater; @@ -44,6 +58,8 @@ import java.security.NoSuchAlgorithmException; public class FDroidApp extends Application { + BluetoothAdapter bluetoothAdapter = null; + private static enum Theme { dark, light } @@ -121,6 +137,7 @@ public class FDroidApp extends Application { } UpdateService.schedule(getApplicationContext()); + bluetoothAdapter = getBluetoothAdapter(); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .discCache(new LimitedAgeDiscCache( @@ -179,4 +196,55 @@ public class FDroidApp extends Application { } } + @TargetApi(18) + private BluetoothAdapter getBluetoothAdapter() { + // to use the new, recommended way of getting the adapter + // http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html + if (Build.VERSION.SDK_INT < 18) + return BluetoothAdapter.getDefaultAdapter(); + else + return ((BluetoothManager) getSystemService(BLUETOOTH_SERVICE)).getAdapter(); + } + + void sendViaBluetooth(Activity activity, int resultCode, String packageName) { + if (resultCode == Activity.RESULT_CANCELED) + return; + String bluetoothPackageName = null; + String className = null; + boolean found = false; + Intent sendBt = null; + try { + PackageManager pm = getPackageManager(); + ApplicationInfo appInfo = pm.getApplicationInfo(packageName, + PackageManager.GET_META_DATA); + sendBt = new Intent(Intent.ACTION_SEND); + // The APK type is blocked by stock Android, so use zip + // sendBt.setType("application/vnd.android.package-archive"); + sendBt.setType("application/zip"); + sendBt.putExtra(Intent.EXTRA_STREAM, + Uri.parse("file://" + appInfo.publicSourceDir)); + // not all devices have the same Bluetooth Activities, so + // let's find it + for (ResolveInfo info : pm.queryIntentActivities(sendBt, 0)) { + bluetoothPackageName = info.activityInfo.packageName; + if (bluetoothPackageName.equals("com.android.bluetooth") + || bluetoothPackageName.equals("com.mediatek.bluetooth")) { + className = info.activityInfo.name; + found = true; + break; + } + } + } catch (NameNotFoundException e1) { + e1.printStackTrace(); + found = false; + } + if (!found) { + Toast.makeText(this, R.string.bluetooth_activity_not_found, + Toast.LENGTH_SHORT).show(); + activity.startActivity(Intent.createChooser(sendBt, getString(R.string.choose_bt_send))); + } else { + sendBt.setClassName(bluetoothPackageName, className); + activity.startActivity(sendBt); + } + } }