From 58ff02a3f4b7b43bc1c1d90e35bb45b94ae77753 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 20 May 2014 12:41:50 +1000 Subject: [PATCH 1/8] Build with dependency on support-appcompat-v7. Thanks to the awesome work of mvdan, this was mostly ready to roll. However, I had to wrestle for a while for two reasons: 1) I forgot to add the dependency in the build.gradle file (it was already present in settings.gradle) 2) My IDE was unable to read the ANDROID_HOME env variable, and despite my internet-search-fu, I couldn't figure out how to make IntelliJ specify env variables for a gradle build. It took a while to figure out, because it was failing silently in weird ways. After slaving away on a nice method to parse both the ANDROID_HOME and the local.properties file (looking for sdk.dir), and then emmiting nice error messages if neither were found or pointed to an invalid location, I discovered it had already be done: android.plugin.sdkDirectory ends up here: https://android.googlesource.com/platform/tools/build/+/master/gradle/src/main/groovy/com/android/build/gradle/internal/Sdk.groovy#161 Which does exactly all that and more. So now sdkLoc is initialized to the value of android.plugin.sdkDirectory. --- AndroidManifest.xml | 2 +- build.gradle | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 280ec4d33..0a372cd23 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -7,7 +7,7 @@ android:versionName="0.69-test" > Date: Tue, 3 Jun 2014 08:13:53 +0930 Subject: [PATCH 2/8] WIP: Migrating activities to appcompat-v7. Conflicts: src/org/fdroid/fdroid/FDroidApp.java src/org/fdroid/fdroid/views/LocalRepoActivity.java src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java --- res/values-v11/styles.xml | 4 +- res/values/styles.xml | 4 +- src/org/fdroid/fdroid/FDroid.java | 8 +- src/org/fdroid/fdroid/FDroidApp.java | 10 +- src/org/fdroid/fdroid/ManageRepo.java | 9 +- .../fdroid/fdroid/NfcNotEnabledActivity.java | 5 +- src/org/fdroid/fdroid/SearchResults.java | 6 +- src/org/fdroid/fdroid/compat/TabManager.java | 178 +++--------------- .../fdroid/views/LocalRepoActivity.java | 28 ++- .../views/QrWizardDownloadActivity.java | 14 +- .../views/QrWizardWifiNetworkActivity.java | 12 +- .../fdroid/views/SelectLocalAppsActivity.java | 19 +- .../fragments/SelectLocalAppsFragment.java | 23 ++- 13 files changed, 105 insertions(+), 215 deletions(-) diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml index 2c6cc4705..6a337d6e4 100644 --- a/res/values-v11/styles.xml +++ b/res/values-v11/styles.xml @@ -1,10 +1,10 @@ - - diff --git a/res/values/styles.xml b/res/values/styles.xml index 003b8f2ff..3bd939192 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -1,10 +1,10 @@ - - diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index a0a622c5c..e66fcc7c9 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -32,9 +32,9 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.FragmentActivity; import android.support.v4.view.MenuItemCompat; import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -42,13 +42,12 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; - import org.fdroid.fdroid.compat.TabManager; import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.views.AppListFragmentPageAdapter; import org.fdroid.fdroid.views.LocalRepoActivity; -public class FDroid extends FragmentActivity { +public class FDroid extends ActionBarActivity { public static final int REQUEST_APPDETAILS = 0; public static final int REQUEST_MANAGEREPOS = 1; @@ -80,6 +79,7 @@ public class FDroid extends FragmentActivity { super.onCreate(savedInstanceState); setContentView(R.layout.fdroid); createViews(); + getTabManager().createTabs(); // Start a search by just typing @@ -333,7 +333,7 @@ public class FDroid extends FragmentActivity { private TabManager getTabManager() { if (tabManager == null) { - tabManager = TabManager.create(this, viewPager); + tabManager = new TabManager(this, viewPager); } return tabManager; } diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 16d936fce..ad0d5ada4 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -32,15 +32,12 @@ import android.os.*; 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.Preferences.ChangeListener; import org.fdroid.fdroid.compat.PRNGFixes; import org.fdroid.fdroid.data.AppProvider; @@ -52,6 +49,11 @@ import org.fdroid.fdroid.net.WifiStateChangeService; import org.thoughtcrime.ssl.pinning.PinningTrustManager; import org.thoughtcrime.ssl.pinning.SystemKeyStore; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import java.io.File; import java.security.*; import java.util.Set; @@ -88,7 +90,7 @@ public class FDroidApp extends Application { public void applyTheme(Activity activity) { switch (curTheme) { case dark: - //activity.setTheme(R.style.AppThemeDark); + activity.setTheme(R.style.AppThemeDark); return; case light: activity.setTheme(R.style.AppThemeLight); diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 64b025597..3db6c204c 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -26,20 +26,19 @@ import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; -import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBarActivity; import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; import android.widget.LinearLayout; import android.widget.Toast; - -import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.views.fragments.RepoListFragment; import java.util.Locale; -public class ManageRepo extends FragmentActivity { + +public class ManageRepo extends ActionBarActivity { /** * If we have a new repo added, or the address of a repo has changed, then @@ -72,7 +71,7 @@ public class ManageRepo extends FragmentActivity { .commit(); } - ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @Override diff --git a/src/org/fdroid/fdroid/NfcNotEnabledActivity.java b/src/org/fdroid/fdroid/NfcNotEnabledActivity.java index bdca0542c..bec4f3840 100644 --- a/src/org/fdroid/fdroid/NfcNotEnabledActivity.java +++ b/src/org/fdroid/fdroid/NfcNotEnabledActivity.java @@ -2,15 +2,16 @@ package org.fdroid.fdroid; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Intent; import android.nfc.NfcAdapter; import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import android.support.v7.app.ActionBarActivity; // aka Android 4.0 aka Ice Cream Sandwich -public class NfcNotEnabledActivity extends Activity { +public class NfcNotEnabledActivity extends ActionBarActivity +{ /* * ACTION_NFC_SETTINGS was added in 4.1 aka Jelly Bean MR1 as a diff --git a/src/org/fdroid/fdroid/SearchResults.java b/src/org/fdroid/fdroid/SearchResults.java index 53f475692..545401a7a 100644 --- a/src/org/fdroid/fdroid/SearchResults.java +++ b/src/org/fdroid/fdroid/SearchResults.java @@ -20,7 +20,7 @@ package org.fdroid.fdroid; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.FragmentActivity; +import android.support.v7.app.ActionBarActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; import android.support.v4.view.MenuItemCompat; @@ -30,7 +30,7 @@ import android.widget.LinearLayout; import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.views.fragments.SearchResultsFragment; -public class SearchResults extends FragmentActivity { +public class SearchResults extends ActionBarActivity { private static final int SEARCH = Menu.FIRST; @@ -61,7 +61,7 @@ public class SearchResults extends FragmentActivity { // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html // for reason why. - ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/src/org/fdroid/fdroid/compat/TabManager.java b/src/org/fdroid/fdroid/compat/TabManager.java index d93e9fdf3..3ddcf3ead 100644 --- a/src/org/fdroid/fdroid/compat/TabManager.java +++ b/src/org/fdroid/fdroid/compat/TabManager.java @@ -1,48 +1,38 @@ package org.fdroid.fdroid.compat; +import android.content.res.Configuration; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Spinner; +import org.fdroid.fdroid.FDroid; + import java.util.ArrayList; import java.util.List; -import android.annotation.TargetApi; -import android.app.ActionBar; -import android.app.FragmentTransaction; -import android.content.res.Configuration; -import android.view.View; -import android.view.ViewGroup; -import android.widget.*; - -import android.support.v4.view.ViewPager; - -import org.fdroid.fdroid.FDroid; -import org.fdroid.fdroid.R; - -public abstract class TabManager extends Compatibility { +public class TabManager { public static final int INDEX_AVAILABLE = 0; public static final int INDEX_INSTALLED = 1; public static final int INDEX_CAN_UPDATE = 2; - public static TabManager create(FDroid parent, ViewPager pager) { - if (hasApi(11)) { - return new HoneycombTabManagerImpl(parent, pager); - } else { - return new OldTabManagerImpl(parent, pager); - } + private ViewPager pager; + private FDroid parent; + private final ActionBar actionBar; + private Spinner actionBarSpinner = null; + + // Used to make sure we only search for the action bar spinner once + // in each orientation. + private boolean dirtyConfig = true; + + public TabManager(FDroid parent, ViewPager pager) { + actionBar = parent.getSupportActionBar(); + this.parent = parent; + this.pager = pager; } - protected final ViewPager pager; - protected final FDroid parent; - - protected TabManager(FDroid parent, ViewPager pager) { - this.parent = parent; - this.pager = pager; - } - - abstract public void createTabs(); - abstract public void selectTab(int index); - abstract public void refreshTabLabel(int index); - abstract public void onConfigurationChanged(Configuration newConfig); - protected CharSequence getLabel(int index) { return pager.getAdapter().getPageTitle(index); } @@ -50,126 +40,7 @@ public abstract class TabManager extends Compatibility { public void removeNotification(int id) { parent.removeNotification(id); } -} -class OldTabManagerImpl extends TabManager { - - private TabHost tabHost; - - public OldTabManagerImpl(FDroid parent, ViewPager pager) { - super(parent, pager); - } - - /** - * There is a bit of boiler-plate code required to get a TabWidget showing, - * which includes creating a TabHost, populating it with the TabWidget, - * and giving it a FrameLayout as a child. This will make the tabs have - * dummy empty contents and then hook them up to our ViewPager. - */ - @Override - public void createTabs() { - tabHost = new TabHost(parent, null); - tabHost.setLayoutParams(new TabHost.LayoutParams( - TabHost.LayoutParams.MATCH_PARENT, TabHost.LayoutParams.WRAP_CONTENT)); - - TabWidget tabWidget = new TabWidget(parent); - tabWidget.setId(android.R.id.tabs); - tabHost.setLayoutParams(new TabHost.LayoutParams( - TabWidget.LayoutParams.MATCH_PARENT, TabWidget.LayoutParams.WRAP_CONTENT)); - - FrameLayout layout = new FrameLayout(parent); - layout.setId(android.R.id.tabcontent); - layout.setLayoutParams(new TabWidget.LayoutParams(0, 0)); - - tabHost.addView(tabWidget); - tabHost.addView(layout); - tabHost.setup(); - - TabHost.TabContentFactory factory = new TabHost.TabContentFactory() { - @Override - public View createTabContent(String tag) { - return new View(parent); - } - }; - - TabHost.TabSpec availableTabSpec = tabHost.newTabSpec("available") - .setIndicator( - parent.getString(R.string.tab_noninstalled), - parent.getResources().getDrawable(android.R.drawable.ic_input_add)) - .setContent(factory); - - TabHost.TabSpec installedTabSpec = tabHost.newTabSpec("installed") - .setIndicator( - parent.getString(R.string.inst), - parent.getResources().getDrawable(android.R.drawable.star_off)) - .setContent(factory); - - TabHost.TabSpec canUpdateTabSpec = tabHost.newTabSpec("canUpdate") - .setIndicator( - parent.getString(R.string.tab_updates), - parent.getResources().getDrawable(android.R.drawable.star_on)) - .setContent(factory); - - tabHost.addTab(availableTabSpec); - tabHost.addTab(installedTabSpec); - tabHost.addTab(canUpdateTabSpec); - - LinearLayout contentView = (LinearLayout)parent.findViewById(R.id.fdroid_layout); - contentView.addView(tabHost, 0); - - tabHost.setOnTabChangedListener( new TabHost.OnTabChangeListener() { - @Override - public void onTabChanged(String tabId) { - int pos = tabHost.getCurrentTab(); - pager.setCurrentItem(pos); - if (pos == INDEX_CAN_UPDATE) - removeNotification(1); - } - }); - } - - - @Override - public void selectTab(int index) { - tabHost.setCurrentTab(index); - if (index == INDEX_CAN_UPDATE) - removeNotification(1); - } - - @Override - public void refreshTabLabel(int index) { - CharSequence text = getLabel(index); - - // Update the count on the 'Updates' tab to show the number available. - // This is quite unpleasant, but seems to be the only way to do it. - TextView textView = (TextView) tabHost.getTabWidget().getChildAt(index) - .findViewById(android.R.id.title); - textView.setText(text); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - // Do nothing - } - -} - -@TargetApi(11) -class HoneycombTabManagerImpl extends TabManager { - - protected final ActionBar actionBar; - private Spinner actionBarSpinner = null; - - // Used to make sure we only search for the action bar spinner once - // in each orientation. - private boolean dirtyConfig = true; - - public HoneycombTabManagerImpl(FDroid parent, ViewPager pager) { - super(parent, pager); - actionBar = parent.getActionBar(); - } - - @Override public void createTabs() { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); for (int i = 0; i < pager.getAdapter().getCount(); i ++) { @@ -198,7 +69,6 @@ class HoneycombTabManagerImpl extends TabManager { } } - @Override public void selectTab(int index) { actionBar.setSelectedNavigationItem(index); Spinner actionBarSpinner = getActionBarSpinner(); @@ -209,13 +79,11 @@ class HoneycombTabManagerImpl extends TabManager { removeNotification(1); } - @Override public void refreshTabLabel(int index) { CharSequence text = getLabel(index); actionBar.getTabAt(index).setText(text); } - @Override public void onConfigurationChanged(Configuration newConfig) { dirtyConfig = true; } diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index 8bb5582aa..e596c4d27 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -5,7 +5,10 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; -import android.content.*; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; import android.net.Uri; import android.net.wifi.WifiManager; @@ -16,12 +19,22 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.ActionBarActivity; import android.text.TextUtils; import android.util.Log; -import android.view.*; -import android.widget.*; - -import org.fdroid.fdroid.*; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.TextView; +import android.widget.Toast; +import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.PreferencesActivity; +import org.fdroid.fdroid.QrGenAsyncTask; +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.localrepo.LocalRepoManager; import org.fdroid.fdroid.localrepo.LocalRepoService; import org.fdroid.fdroid.net.WifiStateChangeService; @@ -30,8 +43,9 @@ import java.util.Locale; import java.util.Timer; import java.util.TimerTask; -public class LocalRepoActivity extends Activity { - private static final String TAG = "LocalRepoActivity"; +public class LocalRepoActivity extends ActionBarActivity { + + private static final String TAG = "org.fdroid.fdroid.LocalRepoActivity"; private ProgressDialog repoProgress; private WifiManager wifiManager; diff --git a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java index 2952be63a..4ebfbf0f2 100644 --- a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java @@ -1,22 +1,26 @@ package org.fdroid.fdroid.views; -import android.app.Activity; -import android.content.*; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; - import org.fdroid.fdroid.*; import org.fdroid.fdroid.net.WifiStateChangeService; -public class QrWizardDownloadActivity extends Activity { - private static final String TAG = "QrWizardDownloadActivity"; +public class QrWizardDownloadActivity extends ActionBarActivity { + + private static final String TAG = "org.fdroid.fdroid.QrWizardDownloadActivity"; @Override public void onCreate(Bundle savedInstanceState) { diff --git a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java index 6e933de92..3f5c8dc4c 100644 --- a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java @@ -1,26 +1,28 @@ package org.fdroid.fdroid.views; -import android.app.Activity; -import android.content.*; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; 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.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; - import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.QrGenAsyncTask; import org.fdroid.fdroid.R; import org.fdroid.fdroid.net.WifiStateChangeService; -public class QrWizardWifiNetworkActivity extends Activity { - private static final String TAG = "QrWizardWifiNetworkActivity"; +public class QrWizardWifiNetworkActivity extends ActionBarActivity { + private static final String TAG = "org.fdroid.fdroid.QrWizardWifiNetworkActivity"; private WifiManager wifiManager; diff --git a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java index 71fc8ee9c..d61f95d4d 100644 --- a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java +++ b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java @@ -1,21 +1,22 @@ package org.fdroid.fdroid.views; -import android.annotation.TargetApi; -import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.view.*; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; 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 Activity { +public class SelectLocalAppsActivity extends ActionBarActivity { + private static final String TAG = "SelectLocalAppsActivity"; private SelectLocalAppsFragment selectLocalAppsFragment = null; private SearchView searchView; @@ -31,14 +32,14 @@ public class SelectLocalAppsActivity extends Activity { protected void onResume() { super.onResume(); if (selectLocalAppsFragment == null) - selectLocalAppsFragment = (SelectLocalAppsFragment) getFragmentManager() + selectLocalAppsFragment = (SelectLocalAppsFragment) getSupportFragmentManager() .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 = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); searchView.setOnQueryTextListener(selectLocalAppsFragment); return true; } diff --git a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java index 785b9ac25..0429f09be 100644 --- a/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/SelectLocalAppsFragment.java @@ -14,24 +14,25 @@ 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; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; import android.text.TextUtils; import android.view.ActionMode; import android.view.View; -import android.widget.*; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.SearchView.OnQueryTextListener; +import android.widget.SimpleCursorAdapter; import android.widget.SimpleCursorAdapter.ViewBinder; - +import android.widget.TextView; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.InstalledAppProvider; @@ -41,10 +42,8 @@ import org.fdroid.fdroid.views.SelectLocalAppsActivity; import java.util.HashSet; -//TODO replace with appcompat-v7 -@TargetApi(11) public class SelectLocalAppsFragment extends ListFragment - implements LoaderCallbacks, OnQueryTextListener { + implements LoaderManager.LoaderCallbacks, OnQueryTextListener { private PackageManager packageManager; private Drawable defaultAppIcon; @@ -95,7 +94,7 @@ public class SelectLocalAppsFragment extends ListFragment Drawable icon; try { icon = packageManager.getApplicationIcon(packageName); - } catch (NameNotFoundException e) { + } catch (PackageManager.NameNotFoundException e) { icon = defaultAppIcon; } iconView.setImageDrawable(icon); From 59b9fd6a8c41f3f15fb7cb78fada156d6fbf8463 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 3 Jun 2014 08:16:18 +0930 Subject: [PATCH 3/8] add PreferenceFragment lib since appcompat lacks such a thing Although Google is encouraging people to make old devices run apps with the action bar (via appcompat-v7), they haven't provided a way for people to create preference/setting screens with an action bar. There are plenty of issues in the Android issue tracker relating to this, but it doesn't yet seem to be on the radar of the Android devs. Until there is a native implementation of PreferenceFragment in the appcompat-v7 support library, this submodule provides is a 3rd party solution. It is actually a fork of the first repo in github, though that was a bit of an upload and dump, without accepting MR's. This fork includes gradle support. --- .gitmodules | 4 ++++ build.gradle | 3 +++ extern/android-support-v4-preferencefragment | 1 + settings.gradle | 1 + 4 files changed, 9 insertions(+) create mode 160000 extern/android-support-v4-preferencefragment diff --git a/.gitmodules b/.gitmodules index c6d0215ad..0228b8e49 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,6 +34,10 @@ path = extern/nanohttpd url = https://github.com/eighthave/nanohttpd ignore = dirty +[submodule "extern/android-support-v4-preferencefragment"] + path = extern/android-support-v4-preferencefragment + url = https://github.com/CyberEagle/android-support-v4-preferencefragment.git + ignore = dirty [submodule "extern/zxing-core"] path = extern/zxing-core url = https://gitlab.com/fdroid/zxing-core.git diff --git a/build.gradle b/build.gradle index 7ccf1a891..d8aebcd23 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,9 @@ dependencies { compile project(':extern:jmdns') compile project(':extern:zipsigner') compile project(':extern:zxing-core') + compile( project(':extern:android-support-v4-preferencefragment') ) { + exclude module: 'support-v4' + } } project(':extern:UniversalImageLoader:library') { diff --git a/extern/android-support-v4-preferencefragment b/extern/android-support-v4-preferencefragment new file mode 160000 index 000000000..fab3cf8a0 --- /dev/null +++ b/extern/android-support-v4-preferencefragment @@ -0,0 +1 @@ +Subproject commit fab3cf8a0699a5ba45b66d4dcddcf7947239de9b diff --git a/settings.gradle b/settings.gradle index f948dc4d2..f7d0cded9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ include ':extern:spongycastle:pg' include ':extern:spongycastle:pkix' include ':extern:spongycastle:prov' include ':extern:zxing-core' +include ':extern:android-support-v4-preferencefragment' include ':support-v4' project(':support-v4').projectDir = new File('extern/Support/v4') From 3050e3dbc50a4851fc8ca6f258538fd11d5c5220 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 3 Jun 2014 08:19:26 +0930 Subject: [PATCH 4/8] Migrating activities to appcompat-v7 The only remaining activity is the AppDetails acvitity, which will require a little more than just making it extend ActionBarActivity. Currently, it extends ListActivity. To support appcompat-v7, it really should have two fragments - the details one and the list one. Then, when the orientation is changed, it should load a different layout with the fragments side by side. Although Google is encouraging people to make old devices run apps with the action bar (via appcompat-v7), they haven't provided a way for people to create preference/setting screens with an action bar. There are plenty of issues in the Android issue tracker relating to this, but it doesn't yet seem to be on the radar of the Android devs. Until there is a native implementation of PreferenceFragment in the appcompat-v7 support library, this submodule provides is a 3rd party solution. It is actually a fork of the first repo in github, though that was a bit of an upload and dump, without accepting MR's. This fork includes gradle support. --- AndroidManifest.xml | 9 +- src/org/fdroid/fdroid/FDroidApp.java | 4 +- src/org/fdroid/fdroid/ManageRepo.java | 2 +- .../fdroid/fdroid/NfcNotEnabledActivity.java | 3 + .../fdroid/fdroid/PreferencesActivity.java | 298 ++---------------- src/org/fdroid/fdroid/SearchResults.java | 4 +- .../fdroid/views/LocalRepoActivity.java | 2 +- .../views/QrWizardDownloadActivity.java | 2 +- .../views/QrWizardWifiNetworkActivity.java | 3 +- .../fdroid/views/RepoDetailsActivity.java | 22 +- .../fdroid/views/SelectLocalAppsActivity.java | 2 +- .../views/fragments/PreferenceFragment.java | 279 ++++++++++++++++ 12 files changed, 338 insertions(+), 292 deletions(-) create mode 100644 src/org/fdroid/fdroid/views/fragments/PreferenceFragment.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0a372cd23..7185fa2d9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -203,6 +203,9 @@ android:launchMode="singleTop" android:parentActivityName=".FDroid" android:screenOrientation="portrait" > + + android:windowSoftInputMode="stateHidden"> + + = 16) { doOnJellybean(intent); diff --git a/src/org/fdroid/fdroid/PreferencesActivity.java b/src/org/fdroid/fdroid/PreferencesActivity.java index 9bf66d4b0..cc7c4940b 100644 --- a/src/org/fdroid/fdroid/PreferencesActivity.java +++ b/src/org/fdroid/fdroid/PreferencesActivity.java @@ -19,288 +19,42 @@ package org.fdroid.fdroid; import android.os.Bundle; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.app.AlertDialog; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.view.MenuItem; +import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBarActivity; +import android.view.MenuItem; +import android.widget.LinearLayout; +import org.fdroid.fdroid.views.fragments.PreferenceFragment; -import org.fdroid.fdroid.Preferences; -import org.fdroid.fdroid.compat.ActionBarCompat; -import org.fdroid.fdroid.installer.CheckRootAsyncTask; -import org.fdroid.fdroid.installer.CheckRootAsyncTask.CheckRootCallback; -import org.fdroid.fdroid.installer.Installer; - -public class PreferencesActivity extends PreferenceActivity implements - OnSharedPreferenceChangeListener { +public class PreferencesActivity extends ActionBarActivity { public static final int RESULT_RESTART = 4; - private int result = 0; - - private static String[] summariesToUpdate = { - Preferences.PREF_UPD_INTERVAL, - Preferences.PREF_UPD_WIFI_ONLY, - Preferences.PREF_UPD_NOTIFY, - Preferences.PREF_UPD_HISTORY, - Preferences.PREF_ROOTED, - Preferences.PREF_INCOMP_VER, - Preferences.PREF_THEME, - Preferences.PREF_PERMISSIONS, - Preferences.PREF_COMPACT_LAYOUT, - Preferences.PREF_IGN_TOUCH, - Preferences.PREF_LOCAL_REPO_BONJOUR, - Preferences.PREF_LOCAL_REPO_NAME, - Preferences.PREF_LOCAL_REPO_HTTPS, - Preferences.PREF_CACHE_APK, - Preferences.PREF_EXPERT, - Preferences.PREF_ROOT_INSTALLER, - Preferences.PREF_SYSTEM_INSTALLER - }; @Override protected void onCreate(Bundle savedInstanceState) { + ((FDroidApp) getApplication()).applyTheme(this); super.onCreate(savedInstanceState); + FragmentManager fm = getSupportFragmentManager(); + if (fm.findFragmentById(android.R.id.content) == null) { + // Need to set a dummy view (which will get overridden by the fragment manager + // below) so that we can call setContentView(). This is a work around for + // a (bug?) thing in 3.0, 3.1 which requires setContentView to be invoked before + // the actionbar is played with: + // http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + setContentView( new LinearLayout(this) ); + + PreferenceFragment preferenceFragment = new PreferenceFragment(); + fm.beginTransaction() + .add(android.R.id.content, preferenceFragment) + .commit(); + } + // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html // for reason why. - ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); - - addPreferencesFromResource(R.xml.preferences); - } - - protected void onoffSummary(String key, int on, int off) { - CheckBoxPreference pref = (CheckBoxPreference)findPreference(key); - if (pref.isChecked()) { - pref.setSummary(on); - } else { - pref.setSummary(off); - } - } - - protected void entrySummary(String key) { - ListPreference pref = (ListPreference)findPreference(key); - pref.setSummary(pref.getEntry()); - } - - protected void textSummary(String key, int resId) { - EditTextPreference pref = (EditTextPreference)findPreference(key); - pref.setSummary(getString(resId, pref.getText())); - } - - protected void updateSummary(String key, boolean changing) { - - if (key.equals(Preferences.PREF_UPD_INTERVAL)) { - ListPreference pref = (ListPreference)findPreference( - Preferences.PREF_UPD_INTERVAL); - int interval = Integer.parseInt(pref.getValue().toString()); - Preference onlyOnWifi = findPreference( - Preferences.PREF_UPD_WIFI_ONLY); - onlyOnWifi.setEnabled(interval > 0); - if (interval == 0) { - pref.setSummary(R.string.update_interval_zero); - } else { - pref.setSummary(pref.getEntry()); - } - - } else if (key.equals(Preferences.PREF_UPD_WIFI_ONLY)) { - onoffSummary(key, R.string.automatic_scan_wifi_on, - R.string.automatic_scan_wifi_off); - - } else if (key.equals(Preferences.PREF_UPD_NOTIFY)) { - onoffSummary(key, R.string.notify_on, - R.string.notify_off); - - } else if (key.equals(Preferences.PREF_UPD_HISTORY)) { - textSummary(key, R.string.update_history_summ); - - } else if (key.equals(Preferences.PREF_PERMISSIONS)) { - onoffSummary(key, R.string.showPermissions_on, - R.string.showPermissions_off); - - } else if (key.equals(Preferences.PREF_COMPACT_LAYOUT)) { - onoffSummary(key, R.string.compactlayout_on, - R.string.compactlayout_off); - - } else if (key.equals(Preferences.PREF_THEME)) { - entrySummary(key); - if (changing) { - result |= RESULT_RESTART; - setResult(result); - } - - } else if (key.equals(Preferences.PREF_INCOMP_VER)) { - onoffSummary(key, R.string.show_incompat_versions_on, - R.string.show_incompat_versions_off); - - } else if (key.equals(Preferences.PREF_ROOTED)) { - onoffSummary(key, R.string.rooted_on, - R.string.rooted_off); - - } else if (key.equals(Preferences.PREF_IGN_TOUCH)) { - onoffSummary(key, R.string.ignoreTouch_on, - R.string.ignoreTouch_off); - - } else if (key.equals(Preferences.PREF_LOCAL_REPO_BONJOUR)) { - onoffSummary(key, R.string.local_repo_bonjour_on, - R.string.local_repo_bonjour_off); - - } else if (key.equals(Preferences.PREF_LOCAL_REPO_NAME)) { - textSummary(key, R.string.local_repo_name_summary); - - } else if (key.equals(Preferences.PREF_LOCAL_REPO_HTTPS)) { - onoffSummary(key, R.string.local_repo_https_on, - R.string.local_repo_https_off); - - } else if (key.equals(Preferences.PREF_CACHE_APK)) { - onoffSummary(key, R.string.cache_downloaded_on, - R.string.cache_downloaded_off); - - } else if (key.equals(Preferences.PREF_EXPERT)) { - onoffSummary(key, R.string.expert_on, - R.string.expert_off); - - } else if (key.equals(Preferences.PREF_ROOT_INSTALLER)) { - onoffSummary(key, R.string.root_installer_on, - R.string.root_installer_off); - - } else if (key.equals(Preferences.PREF_SYSTEM_INSTALLER)) { - onoffSummary(key, R.string.system_installer_on, - R.string.system_installer_off); - - } - } - - /** - * Initializes RootInstaller preference. This method ensures that the preference can only be checked and persisted - * when the user grants root access for F-Droid. - */ - protected void initRootInstallerPreference() { - CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_ROOT_INSTALLER); - - // we are handling persistence ourself! - pref.setPersistent(false); - - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - - @Override - public boolean onPreferenceClick(Preference preference) { - final CheckBoxPreference pref = (CheckBoxPreference) preference; - - if (pref.isChecked()) { - CheckRootAsyncTask checkTask = new CheckRootAsyncTask(PreferencesActivity.this, new CheckRootCallback() { - - @Override - public void onRootCheck(boolean rootGranted) { - if (rootGranted) { - // root access granted - SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_ROOT_INSTALLER, true); - editor.commit(); - pref.setChecked(true); - } else { - // root access denied - SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_ROOT_INSTALLER, false); - editor.commit(); - pref.setChecked(false); - - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(PreferencesActivity.this); - alertBuilder.setTitle(R.string.root_access_denied_title); - alertBuilder.setMessage(PreferencesActivity.this.getString(R.string.root_access_denied_body)); - alertBuilder.setNeutralButton(android.R.string.ok, null); - alertBuilder.create().show(); - } - } - }); - checkTask.execute(); - } else { - SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_ROOT_INSTALLER, false); - editor.commit(); - pref.setChecked(false); - } - - return true; - } - }); - } - - /** - * Initializes SystemInstaller preference, which can only be enabled when F-Droid is installed as a system-app - */ - protected void initSystemInstallerPreference() { - CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_SYSTEM_INSTALLER); - - // we are handling persistence ourself! - pref.setPersistent(false); - - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - - @Override - public boolean onPreferenceClick(Preference preference) { - final CheckBoxPreference pref = (CheckBoxPreference) preference; - - if (pref.isChecked()) { - if (Installer.hasSystemPermissions(PreferencesActivity.this, PreferencesActivity.this.getPackageManager())) { - // system-permission are granted, i.e. F-Droid is a system-app - SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, true); - editor.commit(); - pref.setChecked(true); - } else { - // system-permission not available - SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false); - editor.commit(); - pref.setChecked(false); - - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(PreferencesActivity.this); - alertBuilder.setTitle(R.string.system_permission_denied_title); - alertBuilder.setMessage(PreferencesActivity.this.getString(R.string.system_permission_denied_body)); - alertBuilder.setNeutralButton(android.R.string.ok, null); - alertBuilder.create().show(); - } - } else { - SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); - editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false); - editor.commit(); - pref.setChecked(false); - } - - return true; - } - }); - } - - @Override - protected void onResume() { - super.onResume(); - - getPreferenceScreen().getSharedPreferences() - .registerOnSharedPreferenceChangeListener(this); - - for (String key : summariesToUpdate) { - updateSummary(key, false); - } - - initRootInstallerPreference(); - initSystemInstallerPreference(); - } - - @Override - protected void onPause() { - super.onPause(); - - getPreferenceScreen().getSharedPreferences() - .unregisterOnSharedPreferenceChangeListener(this); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); } @Override @@ -313,10 +67,4 @@ public class PreferencesActivity extends PreferenceActivity implements return super.onOptionsItemSelected(item); } - @Override - public void onSharedPreferenceChanged( - SharedPreferences sharedPreferences, String key) { - updateSummary(key, true); - } - } diff --git a/src/org/fdroid/fdroid/SearchResults.java b/src/org/fdroid/fdroid/SearchResults.java index 545401a7a..08bab2480 100644 --- a/src/org/fdroid/fdroid/SearchResults.java +++ b/src/org/fdroid/fdroid/SearchResults.java @@ -20,14 +20,13 @@ package org.fdroid.fdroid; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; import android.widget.LinearLayout; -import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.views.fragments.SearchResultsFragment; public class SearchResults extends ActionBarActivity { @@ -38,7 +37,6 @@ public class SearchResults extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { ((FDroidApp) getApplication()).applyTheme(this); - super.onCreate(savedInstanceState); // Start a search by just typing diff --git a/src/org/fdroid/fdroid/views/LocalRepoActivity.java b/src/org/fdroid/fdroid/views/LocalRepoActivity.java index e596c4d27..0741eb182 100644 --- a/src/org/fdroid/fdroid/views/LocalRepoActivity.java +++ b/src/org/fdroid/fdroid/views/LocalRepoActivity.java @@ -60,8 +60,8 @@ public class LocalRepoActivity extends ActionBarActivity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); ((FDroidApp) getApplication()).applyTheme(this); + super.onCreate(savedInstanceState); setContentView(R.layout.local_repo_activity); enableWifiButton = (Button) findViewById(R.id.enable_wifi); diff --git a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java index 4ebfbf0f2..dbdd9b4f9 100644 --- a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java @@ -24,8 +24,8 @@ public class QrWizardDownloadActivity extends ActionBarActivity { @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); ((FDroidApp) getApplication()).applyTheme(this); + super.onCreate(savedInstanceState); setContentView(R.layout.qr_wizard_activity); TextView instructions = (TextView) findViewById(R.id.qrWizardInstructions); instructions.setText(R.string.qr_wizard_download_instructions); diff --git a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java index 3f5c8dc4c..5d7419e82 100644 --- a/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardWifiNetworkActivity.java @@ -28,12 +28,13 @@ public class QrWizardWifiNetworkActivity extends ActionBarActivity { @Override public void onCreate(Bundle savedInstanceState) { + ((FDroidApp) getApplication()).applyTheme(this); super.onCreate(savedInstanceState); + wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); 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); diff --git a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java index f0daf679d..495603692 100644 --- a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -10,19 +10,19 @@ import android.nfc.NfcAdapter; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; -import android.support.v4.app.FragmentActivity; +import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBarActivity; import android.util.Log; +import android.view.MenuItem; import android.widget.LinearLayout; import android.widget.Toast; - import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Utils; -import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.views.fragments.RepoDetailsFragment; -public class RepoDetailsActivity extends FragmentActivity { +public class RepoDetailsActivity extends ActionBarActivity { public static final String TAG = "RepoDetailsActivity"; private Repo repo; @@ -33,7 +33,6 @@ public class RepoDetailsActivity extends FragmentActivity { protected void onCreate(Bundle savedInstanceState) { ((FDroidApp) getApplication()).applyTheme(this); - super.onCreate(savedInstanceState); long repoId = getIntent().getLongExtra(RepoDetailsFragment.ARG_REPO_ID, 0); @@ -62,7 +61,7 @@ public class RepoDetailsActivity extends FragmentActivity { }; repo = RepoProvider.Helper.findById(this, repoId, projection); - ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); setTitle(repo.getName()); } @@ -123,4 +122,15 @@ public class RepoDetailsActivity extends FragmentActivity { finish(); } } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + NavUtils.navigateUpFromSameTask(this); + return true; + } + return super.onOptionsItemSelected(item); + } + } diff --git a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java index d61f95d4d..b438915ec 100644 --- a/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java +++ b/src/org/fdroid/fdroid/views/SelectLocalAppsActivity.java @@ -23,8 +23,8 @@ public class SelectLocalAppsActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); ((FDroidApp) getApplication()).applyTheme(this); + super.onCreate(savedInstanceState); setContentView(R.layout.select_local_apps_activity); } diff --git a/src/org/fdroid/fdroid/views/fragments/PreferenceFragment.java b/src/org/fdroid/fdroid/views/fragments/PreferenceFragment.java new file mode 100644 index 000000000..5ef5ca048 --- /dev/null +++ b/src/org/fdroid/fdroid/views/fragments/PreferenceFragment.java @@ -0,0 +1,279 @@ +package org.fdroid.fdroid.views.fragments; + +import android.app.AlertDialog; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.PreferencesActivity; +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.installer.CheckRootAsyncTask; +import org.fdroid.fdroid.installer.Installer; + +public class PreferenceFragment + extends android.support.v4.preference.PreferenceFragment + implements SharedPreferences.OnSharedPreferenceChangeListener { + + private static String[] summariesToUpdate = { + Preferences.PREF_UPD_INTERVAL, + Preferences.PREF_UPD_WIFI_ONLY, + Preferences.PREF_UPD_NOTIFY, + Preferences.PREF_UPD_HISTORY, + Preferences.PREF_ROOTED, + Preferences.PREF_INCOMP_VER, + Preferences.PREF_THEME, + Preferences.PREF_PERMISSIONS, + Preferences.PREF_COMPACT_LAYOUT, + Preferences.PREF_IGN_TOUCH, + Preferences.PREF_LOCAL_REPO_BONJOUR, + Preferences.PREF_LOCAL_REPO_NAME, + Preferences.PREF_LOCAL_REPO_HTTPS, + Preferences.PREF_CACHE_APK, + Preferences.PREF_EXPERT, + Preferences.PREF_ROOT_INSTALLER, + Preferences.PREF_SYSTEM_INSTALLER + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); + } + + protected void onoffSummary(String key, int on, int off) { + CheckBoxPreference pref = (CheckBoxPreference)findPreference(key); + if (pref.isChecked()) { + pref.setSummary(on); + } else { + pref.setSummary(off); + } + } + + protected void entrySummary(String key) { + ListPreference pref = (ListPreference)findPreference(key); + pref.setSummary(pref.getEntry()); + } + + protected void textSummary(String key, int resId) { + EditTextPreference pref = (EditTextPreference)findPreference(key); + pref.setSummary(getString(resId, pref.getText())); + } + + protected void updateSummary(String key, boolean changing) { + + int result = 0; + + if (key.equals(Preferences.PREF_UPD_INTERVAL)) { + ListPreference pref = (ListPreference)findPreference( + Preferences.PREF_UPD_INTERVAL); + int interval = Integer.parseInt(pref.getValue()); + Preference onlyOnWifi = findPreference( + Preferences.PREF_UPD_WIFI_ONLY); + onlyOnWifi.setEnabled(interval > 0); + if (interval == 0) { + pref.setSummary(R.string.update_interval_zero); + } else { + pref.setSummary(pref.getEntry()); + } + + } else if (key.equals(Preferences.PREF_UPD_WIFI_ONLY)) { + onoffSummary(key, R.string.automatic_scan_wifi_on, + R.string.automatic_scan_wifi_off); + + } else if (key.equals(Preferences.PREF_UPD_NOTIFY)) { + onoffSummary(key, R.string.notify_on, + R.string.notify_off); + + } else if (key.equals(Preferences.PREF_UPD_HISTORY)) { + textSummary(key, R.string.update_history_summ); + + } else if (key.equals(Preferences.PREF_PERMISSIONS)) { + onoffSummary(key, R.string.showPermissions_on, + R.string.showPermissions_off); + + } else if (key.equals(Preferences.PREF_COMPACT_LAYOUT)) { + onoffSummary(key, R.string.compactlayout_on, + R.string.compactlayout_off); + + } else if (key.equals(Preferences.PREF_THEME)) { + entrySummary(key); + if (changing) { + result |= PreferencesActivity.RESULT_RESTART; + getActivity().setResult(result); + } + + } else if (key.equals(Preferences.PREF_INCOMP_VER)) { + onoffSummary(key, R.string.show_incompat_versions_on, + R.string.show_incompat_versions_off); + + } else if (key.equals(Preferences.PREF_ROOTED)) { + onoffSummary(key, R.string.rooted_on, + R.string.rooted_off); + + } else if (key.equals(Preferences.PREF_IGN_TOUCH)) { + onoffSummary(key, R.string.ignoreTouch_on, + R.string.ignoreTouch_off); + + } else if (key.equals(Preferences.PREF_LOCAL_REPO_BONJOUR)) { + onoffSummary(key, R.string.local_repo_bonjour_on, + R.string.local_repo_bonjour_off); + + } else if (key.equals(Preferences.PREF_LOCAL_REPO_NAME)) { + textSummary(key, R.string.local_repo_name_summary); + + } else if (key.equals(Preferences.PREF_LOCAL_REPO_HTTPS)) { + onoffSummary(key, R.string.local_repo_https_on, + R.string.local_repo_https_off); + + } else if (key.equals(Preferences.PREF_CACHE_APK)) { + onoffSummary(key, R.string.cache_downloaded_on, + R.string.cache_downloaded_off); + + } else if (key.equals(Preferences.PREF_EXPERT)) { + onoffSummary(key, R.string.expert_on, + R.string.expert_off); + + } else if (key.equals(Preferences.PREF_ROOT_INSTALLER)) { + onoffSummary(key, R.string.root_installer_on, + R.string.root_installer_off); + + } else if (key.equals(Preferences.PREF_SYSTEM_INSTALLER)) { + onoffSummary(key, R.string.system_installer_on, + R.string.system_installer_off); + + } + } + + /** + * Initializes RootInstaller preference. This method ensures that the preference can only be checked and persisted + * when the user grants root access for F-Droid. + */ + protected void initRootInstallerPreference() { + CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_ROOT_INSTALLER); + + // we are handling persistence ourself! + pref.setPersistent(false); + + pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + + @Override + public boolean onPreferenceClick(Preference preference) { + final CheckBoxPreference pref = (CheckBoxPreference) preference; + + if (pref.isChecked()) { + CheckRootAsyncTask checkTask = new CheckRootAsyncTask(getActivity(), new CheckRootAsyncTask.CheckRootCallback() { + + @Override + public void onRootCheck(boolean rootGranted) { + if (rootGranted) { + // root access granted + SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); + editor.putBoolean(Preferences.PREF_ROOT_INSTALLER, true); + editor.commit(); + pref.setChecked(true); + } else { + // root access denied + SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); + editor.putBoolean(Preferences.PREF_ROOT_INSTALLER, false); + editor.commit(); + pref.setChecked(false); + + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); + alertBuilder.setTitle(R.string.root_access_denied_title); + alertBuilder.setMessage(getActivity().getString(R.string.root_access_denied_body)); + alertBuilder.setNeutralButton(android.R.string.ok, null); + alertBuilder.create().show(); + } + } + }); + checkTask.execute(); + } else { + SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); + editor.putBoolean(Preferences.PREF_ROOT_INSTALLER, false); + editor.commit(); + pref.setChecked(false); + } + + return true; + } + }); + } + + /** + * Initializes SystemInstaller preference, which can only be enabled when F-Droid is installed as a system-app + */ + protected void initSystemInstallerPreference() { + CheckBoxPreference pref = (CheckBoxPreference) findPreference(Preferences.PREF_SYSTEM_INSTALLER); + + // we are handling persistence ourself! + pref.setPersistent(false); + + pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + + @Override + public boolean onPreferenceClick(Preference preference) { + final CheckBoxPreference pref = (CheckBoxPreference) preference; + + if (pref.isChecked()) { + if (Installer.hasSystemPermissions(getActivity(), getActivity().getPackageManager())) { + // system-permission are granted, i.e. F-Droid is a system-app + SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); + editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, true); + editor.commit(); + pref.setChecked(true); + } else { + // system-permission not available + SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); + editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false); + editor.commit(); + pref.setChecked(false); + + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); + alertBuilder.setTitle(R.string.system_permission_denied_title); + alertBuilder.setMessage(getActivity().getString(R.string.system_permission_denied_body)); + alertBuilder.setNeutralButton(android.R.string.ok, null); + alertBuilder.create().show(); + } + } else { + SharedPreferences.Editor editor = pref.getSharedPreferences().edit(); + editor.putBoolean(Preferences.PREF_SYSTEM_INSTALLER, false); + editor.commit(); + pref.setChecked(false); + } + + return true; + } + }); + } + + @Override + public void onResume() { + super.onResume(); + + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + + for (String key : summariesToUpdate) { + updateSummary(key, false); + } + + initRootInstallerPreference(); + initSystemInstallerPreference(); + } + + @Override + public void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + updateSummary(key, true); + } + + +} From 659b46fd4e6fc937a0381c9fb75c95df4cdb8307 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 27 May 2014 06:30:09 +0930 Subject: [PATCH 5/8] Fix to sdkLoc in build. Added light (without dark action bar) theme. NOTE: I don't know how android will go with adding a new property to a string-array resource, but not having it translated everywhere. Will it struggle because the EN version has three values for "theme", but other translations only have two? --- build.gradle | 17 ++++++++++++++++- res/values/array.xml | 1 + res/values/no_trans.xml | 1 + res/values/styles.xml | 10 +++++++++- src/org/fdroid/fdroid/FDroidApp.java | 5 ++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d8aebcd23..18ba173d6 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { apply plugin: 'android' def toolVersion = "19.1" -sdkLoc = android.plugin.sdkDirectory +sdkLoc = getSdkPath() FileCollection getAndroidPrebuilt(String apiLevel) { files("$sdkLoc/platforms/android-$apiLevel/android.jar") @@ -142,3 +142,18 @@ subprojects { } } } + +/** + * Currently a bit hacky, because android.plugin is protected. + * The end goal is to find something in the android BaseExtension class found here: + * https://android.googlesource.com/platform/tools/build/+/master/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.groovy + * which ends up asking their Sdk class for it's location. That class knows about + * both ANDROID_HOME env variables, and also local.properties sdk.loc properties. + * + * If in the future, the android.adbExe is found to be inappropriate, deprecated, + * or a better way of finding the sdk path exists, we can change the implementation + * of this method to reflect that. + */ +def getSdkPath() { + new File( "$android.adbExe/../../" ).canonicalPath +} diff --git a/res/values/array.xml b/res/values/array.xml index bea7d606e..7aed61a82 100644 --- a/res/values/array.xml +++ b/res/values/array.xml @@ -11,5 +11,6 @@ Dark Light + Light (with dark action bar) diff --git a/res/values/no_trans.xml b/res/values/no_trans.xml index 102cdab50..0f8e66e48 100644 --- a/res/values/no_trans.xml +++ b/res/values/no_trans.xml @@ -25,6 +25,7 @@ dark light + lightWithDarkActionBar diff --git a/res/values/styles.xml b/res/values/styles.xml index 3bd939192..dfd911baa 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -4,7 +4,11 @@ - + + @@ -25,4 +29,8 @@ + + diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index d96a7e7c2..54111e958 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -76,7 +76,7 @@ public class FDroidApp extends Application { BluetoothAdapter bluetoothAdapter = null; private static enum Theme { - dark, light + dark, light, lightWithDarkActionBar } private static Theme curTheme = Theme.dark; @@ -95,6 +95,9 @@ public class FDroidApp extends Application { case light: activity.setTheme(R.style.AppThemeLight); break; + case lightWithDarkActionBar: + activity.setTheme(R.style.AppThemeLightWithDarkActionBar); + break; } } From b82be525b955d8ef848a85d70c49f8cffc942253 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 3 Jun 2014 08:26:14 +0930 Subject: [PATCH 6/8] Rotation of app details with fragments implemented. This was a bit more complex than all the other views, because it supports rotation, and different views for when it is rotated. The end result is that the way in which the views were constructed needed to be completely redone. In the process, I also moved the layout of the app summary to a Relative Layout. This adds more flexibility, and is also the suggested layout for complex views (as apposed to nested linear layouts). I believe this is due to the performance of relative vs linear layotus. It was aprticularly hard to figure out what was going on when rotating an Activity which had a list fragment that had another fragment as a header. I don't think fragments were designed to work like this, but I believe it is all working as expected now. Conflicts: src/org/fdroid/fdroid/Preferences.java --- res/layout-land/app_details.xml | 34 ++ res/layout-land/appdetails.xml | 91 ---- res/layout/app_details.xml | 16 + res/layout/app_details_summary.xml | 154 ++++++ res/layout/appdetails.xml | 104 ---- res/values/ids.xml | 1 + src/org/fdroid/fdroid/AppDetails.java | 698 +++++++++++++++---------- src/org/fdroid/fdroid/Preferences.java | 25 +- src/org/fdroid/fdroid/Utils.java | 59 ++- 9 files changed, 695 insertions(+), 487 deletions(-) create mode 100644 res/layout-land/app_details.xml delete mode 100644 res/layout-land/appdetails.xml create mode 100644 res/layout/app_details.xml create mode 100644 res/layout/app_details_summary.xml delete mode 100644 res/layout/appdetails.xml diff --git a/res/layout-land/app_details.xml b/res/layout-land/app_details.xml new file mode 100644 index 000000000..d7ff9d84f --- /dev/null +++ b/res/layout-land/app_details.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/res/layout-land/appdetails.xml b/res/layout-land/appdetails.xml deleted file mode 100644 index 9f91b96ff..000000000 --- a/res/layout-land/appdetails.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/layout/app_details.xml b/res/layout/app_details.xml new file mode 100644 index 000000000..3acfc0973 --- /dev/null +++ b/res/layout/app_details.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/res/layout/app_details_summary.xml b/res/layout/app_details_summary.xml new file mode 100644 index 000000000..44c9afe11 --- /dev/null +++ b/res/layout/app_details_summary.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/appdetails.xml b/res/layout/appdetails.xml deleted file mode 100644 index 686eb91d5..000000000 --- a/res/layout/appdetails.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/values/ids.xml b/res/values/ids.xml index 23d937f3d..2d82ad59c 100644 --- a/res/values/ids.xml +++ b/res/values/ids.xml @@ -1,4 +1,5 @@ + \ No newline at end of file diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index fca4c0b10..60a3851b6 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -21,10 +21,12 @@ package org.fdroid.fdroid; import android.app.Activity; import android.app.AlertDialog; -import android.app.ListActivity; import android.app.ProgressDialog; import android.bluetooth.BluetoothAdapter; -import android.content.*; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -34,12 +36,12 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; +import android.support.v4.app.ListFragment; import android.support.v4.app.NavUtils; import android.support.v4.view.MenuItemCompat; -import android.text.Editable; +import android.support.v7.app.ActionBarActivity; import android.text.Html; -import android.text.Html.TagHandler; import android.text.Spanned; import android.text.format.DateFormat; import android.text.method.LinkMovementMethod; @@ -52,8 +54,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.ArrayAdapter; +import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; @@ -64,20 +66,45 @@ 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 org.fdroid.fdroid.data.*; +import org.fdroid.fdroid.data.Apk; +import org.fdroid.fdroid.data.ApkProvider; +import org.fdroid.fdroid.data.App; +import org.fdroid.fdroid.data.AppProvider; +import org.fdroid.fdroid.data.Repo; +import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.Installer.AndroidNotCompatibleException; import org.fdroid.fdroid.installer.Installer.InstallerCallback; import org.fdroid.fdroid.net.ApkDownloader; import org.fdroid.fdroid.net.Downloader; -import org.xml.sax.XMLReader; import java.io.File; import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.List; -public class AppDetails extends ListActivity implements ProgressListener { +interface AppDetailsData { + public App getApp(); + public AppDetails.ApkListAdapter getApks(); + public Signature getInstalledSignature(); + public String getInstalledSignatureId(); +} + +/** + * Interface which allows the apk list fragment to communicate with the activity when + * a user requests to install/remove an apk by clicking on an item in the list. + * + * NOTE: This is not to do with with the sudo/packagemanager/other installer + * stuff which allows multiple ways to install apps. It is only here to make fragment- + * activity communication possible. + */ +interface AppInstallListener { + public void install(final Apk apk); + public void removeApk(String packageName); +} + +public class AppDetails extends ActionBarActivity implements ProgressListener, AppDetailsData, AppInstallListener { + private static final String TAG = "org.fdroid.fdroid.AppDetails"; public static final int REQUEST_ENABLE_BLUETOOTH = 2; @@ -118,14 +145,13 @@ public class AppDetails extends ListActivity implements ProgressListener { AppDetails.this.finish(); return; } - updateViews(); + refreshApkList(); MenuManager.create(AppDetails.this).invalidateOptionsMenu(); - } + } } - - private class ApkListAdapter extends ArrayAdapter { + class ApkListAdapter extends ArrayAdapter { private LayoutInflater mInflater = (LayoutInflater) mctx.getSystemService( Context.LAYOUT_INFLATER_SERVICE); @@ -134,7 +160,7 @@ public class AppDetails extends ListActivity implements ProgressListener { super(context, 0); List apks = ApkProvider.Helper.findByApp(context, app.id); for (Apk apk : apks ) { - if (apk.compatible || pref_incompatibleVersions) { + if (apk.compatible || Preferences.get().showIncompatibleVersions()) { add(apk); } } @@ -149,7 +175,7 @@ public class AppDetails extends ListActivity implements ProgressListener { ViewHolder holder; if (convertView == null) { - convertView = mInflater.inflate(R.layout.apklistitem, null); + convertView = mInflater.inflate(R.layout.apklistitem, parent, false); holder = new ViewHolder(); holder.version = (TextView) convertView.findViewById(R.id.version); @@ -185,7 +211,7 @@ public class AppDetails extends ListActivity implements ProgressListener { holder.size.setVisibility(View.GONE); } - if (!pref_expert) { + if (!Preferences.get().expertMode()) { holder.api.setVisibility(View.GONE); } else if (apk.minSdkVersion > 0 && apk.maxSdkVersion > 0) { holder.api.setText(getString(R.string.minsdk_up_to_maxsdk, @@ -216,7 +242,7 @@ public class AppDetails extends ListActivity implements ProgressListener { holder.added.setVisibility(View.GONE); } - if (pref_expert && apk.nativecode != null) { + if (Preferences.get().expertMode() && apk.nativecode != null) { holder.nativecode.setText(apk.nativecode.toString().replaceAll(","," ")); holder.nativecode.setVisibility(View.VISIBLE); } else { @@ -277,11 +303,7 @@ public class AppDetails extends ListActivity implements ProgressListener { private boolean startingIgnoreAll; private int startingIgnoreThis; - LinearLayout headerView; - View infoView; - private final Context mctx = this; - private DisplayImageOptions displayImageOptions; private Installer installer; /** @@ -337,9 +359,9 @@ public class AppDetails extends ListActivity implements ProgressListener { // fdroid.app:app.id appId = data.getEncodedSchemeSpecificPart(); } - Log.d("FDroid", "AppDetails launched from link, for '" + appId + "'"); + Log.d(TAG, "AppDetails launched from link, for '" + appId + "'"); } else if (!i.hasExtra(EXTRA_APPID)) { - Log.e("FDroid", "No application ID in AppDetails!?"); + Log.e(TAG, "No application ID in AppDetails!?"); } else { appId = i.getStringExtra(EXTRA_APPID); } @@ -349,39 +371,22 @@ public class AppDetails extends ListActivity implements ProgressListener { @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - + fdroidApp = ((FDroidApp) getApplication()); fdroidApp.applyTheme(this); super.onCreate(savedInstanceState); - displayImageOptions = new DisplayImageOptions.Builder() - .cacheInMemory(true) - .cacheOnDisk(true) - .imageScaleType(ImageScaleType.NONE) - .showImageOnLoading(R.drawable.ic_repo_app_default) - .showImageForEmptyUri(R.drawable.ic_repo_app_default) - .bitmapConfig(Bitmap.Config.RGB_565) - .build(); - - setContentView(R.layout.appdetails); - - // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) - // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html - // for reason why. - ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); - if (getIntent().hasExtra(EXTRA_FROM)) { setTitle(getIntent().getStringExtra(EXTRA_FROM)); } mPm = getPackageManager(); - installer = Installer.getActivityInstaller(this, mPm, - myInstallerCallback); + installer = Installer.getActivityInstaller(this, mPm, myInstallerCallback); // Get the preferences we're going to use in this Activity... - ConfigurationChangeHelper previousData = (ConfigurationChangeHelper)getLastNonConfigurationInstance(); + ConfigurationChangeHelper previousData = (ConfigurationChangeHelper)getLastCustomNonConfigurationInstance(); if (previousData != null) { Log.d(TAG, "Recreating view after configuration change."); downloadHandler = previousData.downloader; @@ -397,28 +402,34 @@ public class AppDetails extends ListActivity implements ProgressListener { } } - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); - pref_expert = prefs.getBoolean(Preferences.PREF_EXPERT, false); - pref_permissions = prefs.getBoolean(Preferences.PREF_PERMISSIONS, false); - pref_incompatibleVersions = prefs.getBoolean( - Preferences.PREF_INCOMP_VER, false); - // Set up the list... - headerView = new LinearLayout(this); - ListView lv = (ListView) findViewById(android.R.id.list); - lv.addHeaderView(headerView); adapter = new ApkListAdapter(this, app); - setListAdapter(adapter); - startViews(); + // Wait until all other intialization before doing this, because it will create the + // fragments, which rely on data from the activity that is set earlier in this method. + setContentView(R.layout.app_details); + + // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) + // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + // for reason why. + ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); + + // Check for the presence of a view which only exists in the landscape view. + // This seems to be the preferred way to interrogate the view, rather than + // to check the orientation. I guess this is because views can be dynamically + // chosen based on more than just orientation (e.g. large screen sizes). + View onlyInLandscape = findViewById(R.id.app_summary_container); + + AppDetailsListFragment listFragment = + (AppDetailsListFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_app_list); + if (onlyInLandscape == null) { + listFragment.setupSummaryHeader(); + } else { + listFragment.removeSummaryHeader(); + } } - private boolean pref_expert; - private boolean pref_permissions; - private boolean pref_incompatibleVersions; - // The signature of the installed version. private Signature mInstalledSignature; private String mInstalledSigID; @@ -426,13 +437,13 @@ public class AppDetails extends ListActivity implements ProgressListener { @Override protected void onResume() { super.onResume(); - + // register observer to know when install status changes myAppObserver = new AppObserver(new Handler()); getContentResolver().registerContentObserver( - AppProvider.getContentUri(app.id), - true, - myAppObserver); + AppProvider.getContentUri(app.id), + true, + myAppObserver); if (downloadHandler != null) { if (downloadHandler.isComplete()) { downloadCompleteInstallApk(); @@ -446,9 +457,12 @@ public class AppDetails extends ListActivity implements ProgressListener { updateProgressDialog(); } } + } - updateViews(); - + @Override + protected void onResumeFragments() { + super.onResumeFragments(); + refreshApkList(); MenuManager.create(this).invalidateOptionsMenu(); } @@ -509,7 +523,7 @@ public class AppDetails extends ListActivity implements ProgressListener { @Override - public Object onRetainNonConfigurationInstance() { + public Object onRetainCustomNonConfigurationInstance() { inProcessOfChangingConfiguration = true; return new ConfigurationChangeHelper(downloadHandler, app); } @@ -538,7 +552,7 @@ public class AppDetails extends ListActivity implements ProgressListener { // Return true if the app was found, false otherwise. private boolean reset(String appId) { - Log.d("FDroid", "Getting application details for " + appId); + Log.d(TAG, "Getting application details for " + appId); App newApp = null; if (appId != null && appId.length() > 0) { @@ -579,233 +593,22 @@ public class AppDetails extends ListActivity implements ProgressListener { Hasher hash = new Hasher("MD5", mInstalledSignature.toCharsString().getBytes()); mInstalledSigID = hash.getHash(); } catch (NameNotFoundException e) { - Log.d("FDroid", "Failed to get installed signature"); + Log.d(TAG, "Failed to get installed signature"); } catch (NoSuchAlgorithmException e) { - Log.d("FDroid", "Failed to calculate signature MD5 sum"); + Log.d(TAG, "Failed to calculate signature MD5 sum"); mInstalledSignature = null; } } } - private void startViews() { - - // Insert the 'infoView' (which contains the summary, various odds and - // ends, and the description) into the appropriate place, if we're in - // landscape mode. In portrait mode, we put it in the listview's - // header.. - infoView = View.inflate(this, R.layout.appinfo, null); - LinearLayout landparent = (LinearLayout) findViewById(R.id.landleft); - headerView.removeAllViews(); - if (landparent != null) { - landparent.addView(infoView); - Log.d("FDroid", "Setting up landscape view"); - } else { - headerView.addView(infoView); - Log.d("FDroid", "Setting up portrait view"); - } - - // Set the icon... - ImageView iv = (ImageView) findViewById(R.id.icon); - ImageLoader.getInstance().displayImage(app.iconUrl, iv, - displayImageOptions); - - // Set the title and other header details... - TextView tv = (TextView) findViewById(R.id.title); - tv.setText(app.name); - tv = (TextView) findViewById(R.id.license); - tv.setText(app.license); - - if (app.categories != null) { - tv = (TextView) findViewById(R.id.categories); - tv.setText(app.categories.toString().replaceAll(",",", ")); - } - - tv = (TextView) infoView.findViewById(R.id.description); - - tv.setMovementMethod(LinkMovementMethod.getInstance()); - - // Need this to add the unimplemented support for ordered and unordered - // lists to Html.fromHtml(). - class HtmlTagHandler implements TagHandler { - int listNum; - - @Override - public void handleTag(boolean opening, String tag, Editable output, - XMLReader reader) { - if (tag.equals("ul")) { - if (opening) - listNum = -1; - else - output.append('\n'); - } else if (opening && tag.equals("ol")) { - if (opening) - listNum = 1; - else - output.append('\n'); - } else if (tag.equals("li")) { - if (opening) { - if (listNum == -1) { - output.append("\t• "); - } else { - output.append("\t").append(Integer.toString(listNum)).append(". "); - listNum++; - } - } else { - output.append('\n'); - } - } - } - } - Spanned desc = Html.fromHtml( - app.description, null, new HtmlTagHandler()); - tv.setText(desc.subSequence(0, desc.length() - 2)); - - tv = (TextView) infoView.findViewById(R.id.appid); - if (pref_expert) - tv.setText(app.id); - else - tv.setVisibility(View.GONE); - - tv = (TextView) infoView.findViewById(R.id.summary); - tv.setText(app.summary); - - Apk curApk = null; - for (int i = 0; i < adapter.getCount(); i ++) { - Apk apk = adapter.getItem(i); - if (apk.vercode == app.suggestedVercode) { - curApk = apk; - break; - } - } - - if (pref_permissions && !adapter.isEmpty() && - ((curApk != null && curApk.compatible) || pref_incompatibleVersions)) { - tv = (TextView) infoView.findViewById(R.id.permissions_list); - - CommaSeparatedList permsList = adapter.getItem(0).permissions; - if (permsList == null) { - tv.setText(getString(R.string.no_permissions)); - } else { - Iterator permissions = permsList.iterator(); - StringBuilder sb = new StringBuilder(); - while (permissions.hasNext()) { - String permissionName = permissions.next(); - try { - Permission permission = new Permission(this, permissionName); - sb.append("\t• ").append(permission.getName()).append('\n'); - } catch (NameNotFoundException e) { - if (permissionName.equals("ACCESS_SUPERUSER")) { - sb.append("\t• Full permissions to all device features and storage\n"); - } else { - Log.d("FDroid", "Permission not yet available: " + permissionName); - } - } - } - if (sb.length() > 0) sb.setLength(sb.length() - 1); - tv.setText(sb.toString()); - } - tv = (TextView) infoView.findViewById(R.id.permissions); - tv.setText(getString( - R.string.permissions_for_long, adapter.getItem(0).version)); - } else { - infoView.findViewById(R.id.permissions).setVisibility(View.GONE); - infoView.findViewById(R.id.permissions_list).setVisibility(View.GONE); - } - - tv = (TextView) infoView.findViewById(R.id.antifeatures); - if (app.antiFeatures != null) { - StringBuilder sb = new StringBuilder(); - for (String af : app.antiFeatures) { - String afdesc = descAntiFeature(af); - if (afdesc != null) { - sb.append("\t• ").append(afdesc).append("\n"); - } - } - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - tv.setText(sb.toString()); - } else { - tv.setVisibility(View.GONE); - } - } else { - tv.setVisibility(View.GONE); - } - } - - private String descAntiFeature(String af) { - if (af.equals("Ads")) - return getString(R.string.antiadslist); - if (af.equals("Tracking")) - return getString(R.string.antitracklist); - if (af.equals("NonFreeNet")) - return getString(R.string.antinonfreenetlist); - if (af.equals("NonFreeAdd")) - return getString(R.string.antinonfreeadlist); - if (af.equals("NonFreeDep")) - return getString(R.string.antinonfreedeplist); - if (af.equals("UpstreamNonFree")) - return getString(R.string.antiupstreamnonfreelist); - return null; - } - - private void updateViews() { - - // Refresh the list... + private void refreshApkList() { adapter.notifyDataSetChanged(); - - TextView tv = (TextView) findViewById(R.id.status); - 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) { - tv.setVisibility(View.VISIBLE); - tv.setText("Signed: " + mInstalledSigID); - } else { - tv.setVisibility(View.GONE); - } - - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - final Apk apk = adapter.getItem(position - l.getHeaderViewsCount()); - if (app.installedVersionCode == apk.vercode) - removeApk(app.id); - else if (app.installedVersionCode > apk.vercode) { - AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this); - ask_alrt.setMessage(getString(R.string.installDowngrade)); - ask_alrt.setPositiveButton(getString(R.string.yes), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int whichButton) { - install(apk); - } - }); - ask_alrt.setNegativeButton(getString(R.string.no), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int whichButton) { - } - }); - AlertDialog alert = ask_alrt.create(); - alert.show(); - } else - install(apk); } @Override public boolean onPrepareOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); + super.onPrepareOptionsMenu(menu); menu.clear(); if (app == null) return true; @@ -997,8 +800,7 @@ public class AppDetails extends ListActivity implements ProgressListener { } // Install the version of this app denoted by 'app.curApk'. - private void install(final Apk apk) { - final Activity activity = this; + public void install(final Apk apk) { String [] projection = { RepoProvider.DataColumns.ADDRESS }; Repo repo = RepoProvider.Helper.findById(this, apk.repo, projection); if (repo == null || repo.address == null) { @@ -1064,7 +866,8 @@ public class AppDetails extends ListActivity implements ProgressListener { } } - private void removeApk(String packageName) { + @Override + public void removeApk(String packageName) { setProgressBarIndeterminateVisibility(true); try { @@ -1253,4 +1056,327 @@ public class AppDetails extends ListActivity implements ProgressListener { break; } } -} + + public App getApp() { + return app; + } + + public ApkListAdapter getApks() { + return adapter; + } + + public Signature getInstalledSignature() { + return mInstalledSignature; + } + + public String getInstalledSignatureId() { + return mInstalledSigID; + } + + public static class AppDetailsSummaryFragment extends Fragment { + + protected final Preferences prefs; + protected final DisplayImageOptions displayImageOptions; + private AppDetailsData data; + + public AppDetailsSummaryFragment() { + prefs = Preferences.get(); + displayImageOptions = new DisplayImageOptions.Builder() + .cacheInMemory(true) + .cacheOnDisk(true) + .imageScaleType(ImageScaleType.NONE) + .showImageOnLoading(R.drawable.ic_repo_app_default) + .showImageForEmptyUri(R.drawable.ic_repo_app_default) + .bitmapConfig(Bitmap.Config.RGB_565) + .build(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + data = (AppDetailsData)activity; + } + + protected App getApp() { + return data.getApp(); + } + + protected ApkListAdapter getApks() { + return data.getApks(); + } + + protected Signature getInstalledSignature() { + return data.getInstalledSignature(); + } + + protected String getInstalledSignatureId() { + return data.getInstalledSignatureId(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View summaryView = inflater.inflate(R.layout.app_details_summary, container, false); + setupView(summaryView); + return summaryView; + } + + @Override + public void onResume() { + super.onResume(); + updateViews(getView()); + } + + private void setupView(View view) { + + // Set the icon... + ImageView iv = (ImageView) view.findViewById(R.id.icon); + ImageLoader.getInstance().displayImage(getApp().iconUrl, iv, displayImageOptions); + + // Set the title and other header details... + TextView tv = (TextView) view.findViewById(R.id.title); + tv.setText(getApp().name); + tv = (TextView) view.findViewById(R.id.license); + tv.setText(getApp().license); + + if (getApp().categories != null) { + tv = (TextView) view.findViewById(R.id.categories); + tv.setText(getApp().categories.toString().replaceAll(",", ", ")); + } + + TextView description = (TextView) view.findViewById(R.id.description); + Spanned desc = Html.fromHtml(getApp().description, null, new Utils.HtmlTagHandler()); + description.setMovementMethod(LinkMovementMethod.getInstance()); + description.setText(desc.subSequence(0, desc.length() - 2)); + + TextView appIdView = (TextView) view.findViewById(R.id.appid); + if (prefs.expertMode()) + appIdView.setText(getApp().id); + else + appIdView.setVisibility(View.GONE); + + TextView summaryView = (TextView) view.findViewById(R.id.summary); + summaryView.setText(getApp().summary); + + Apk curApk = null; + for (int i = 0; i < getApks().getCount(); i ++) { + Apk apk = getApks().getItem(i); + if (apk.vercode == getApp().suggestedVercode) { + curApk = apk; + break; + } + } + + TextView permissionListView = (TextView) view.findViewById(R.id.permissions_list); + TextView permissionHeader = (TextView) view.findViewById(R.id.permissions); + boolean curApkCompatible = curApk != null && curApk.compatible; + if (prefs.showPermissions() && !getApks().isEmpty() && + ( curApkCompatible || prefs.showIncompatibleVersions() ) ) { + + CommaSeparatedList permsList = getApks().getItem(0).permissions; + if (permsList == null) { + permissionListView.setText(getString(R.string.no_permissions)); + } else { + Iterator permissions = permsList.iterator(); + StringBuilder sb = new StringBuilder(); + while (permissions.hasNext()) { + String permissionName = permissions.next(); + try { + Permission permission = new Permission(getActivity(), permissionName); + // TODO: Make this list RTL friendly + sb.append("\t• ").append(permission.getName()).append('\n'); + } catch (NameNotFoundException e) { + if (permissionName.equals("ACCESS_SUPERUSER")) { + // TODO: i18n this string, but surely it is already translated somewhere? + sb.append("\t• Full permissions to all device features and storage\n"); + } else { + Log.e(TAG, "Permission not yet available: " + permissionName); + } + } + } + if (sb.length() > 0) sb.setLength(sb.length() - 1); + permissionListView.setText(sb.toString()); + } + permissionHeader.setText(getString(R.string.permissions_for_long, getApks().getItem(0).version)); + } else { + permissionListView.setVisibility(View.GONE); + permissionHeader.setVisibility(View.GONE); + } + + TextView antiFeaturesView = (TextView) view.findViewById(R.id.antifeatures); + if (getApp().antiFeatures != null) { + StringBuilder sb = new StringBuilder(); + for (String af : getApp().antiFeatures) { + String afdesc = descAntiFeature(af); + if (afdesc != null) { + sb.append("\t• ").append(afdesc).append("\n"); + } + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + antiFeaturesView.setText(sb.toString()); + } else { + antiFeaturesView.setVisibility(View.GONE); + } + } else { + antiFeaturesView.setVisibility(View.GONE); + } + + updateViews(view); + } + + private String descAntiFeature(String af) { + if (af.equals("Ads")) + return getString(R.string.antiadslist); + if (af.equals("Tracking")) + return getString(R.string.antitracklist); + if (af.equals("NonFreeNet")) + return getString(R.string.antinonfreenetlist); + if (af.equals("NonFreeAdd")) + return getString(R.string.antinonfreeadlist); + if (af.equals("NonFreeDep")) + return getString(R.string.antinonfreedeplist); + if (af.equals("UpstreamNonFree")) + return getString(R.string.antiupstreamnonfreelist); + return null; + } + + public void updateViews(View view) { + + if (view == null) { + Log.e(TAG, "AppDetailsSummaryFragment.refreshApkList - view == null. Oops."); + return; + } + + TextView statusView = (TextView) view.findViewById(R.id.status); + if (getApp().isInstalled()) { + statusView.setText(getString(R.string.details_installed, getApp().installedVersionName)); + NfcBeamManager.setAndroidBeam(getActivity(), getApp().id); + } else { + statusView.setText(getString(R.string.details_notinstalled)); + NfcBeamManager.disableAndroidBeam(getActivity()); + } + + TextView signatureView = (TextView) view.findViewById(R.id.signature); + if (prefs.expertMode() && getInstalledSignature() != null) { + signatureView.setVisibility(View.VISIBLE); + signatureView.setText("Signed: " + getInstalledSignatureId()); + } else { + signatureView.setVisibility(View.GONE); + } + + } + } + + public static class AppDetailsListFragment extends ListFragment { + + private final String SUMMARY_TAG = "summary"; + + private AppDetailsData data; + private AppInstallListener installListener; + private AppDetailsSummaryFragment summaryFragment = null; + + private FrameLayout headerView; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + data = (AppDetailsData)activity; + installListener = (AppInstallListener)activity; + } + + protected void install(final Apk apk) { + installListener.install(apk); + } + + protected void remove() { + installListener.removeApk(getApp().id); + } + + protected App getApp() { + return data.getApp(); + } + + protected ApkListAdapter getApks() { + return data.getApks(); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + // A bit of a hack, but we can't add the header view in setupSummaryHeader(), + // due to the fact it needs to happen before setListAdapter(). Also, seeing + // as we may never add a summary header (i.e. in landscape), this is probably + // the last opportunity to set the list adapter. As such, we use the headerView + // as a mechanism to optionally allow adding a header in the future. + if (headerView == null) { + headerView = new FrameLayout(getActivity().getApplicationContext()); + headerView.setId(R.id.appDetailsSummaryHeader); + } else { + Fragment summaryFragment = getChildFragmentManager().findFragmentByTag(SUMMARY_TAG); + if (summaryFragment != null) { + getChildFragmentManager().beginTransaction().remove(summaryFragment).commit(); + } + } + + setListAdapter(null); + getListView().addHeaderView(headerView); + setListAdapter(getApks()); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + final Apk apk = getApks().getItem(position - l.getHeaderViewsCount()); + if (getApp().installedVersionCode == apk.vercode) + remove(); + else if (getApp().installedVersionCode > apk.vercode) { + AlertDialog.Builder ask_alrt = new AlertDialog.Builder(getActivity()); + ask_alrt.setMessage(getString(R.string.installDowngrade)); + ask_alrt.setPositiveButton(getString(R.string.yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, + int whichButton) { + install(apk); + } + }); + ask_alrt.setNegativeButton(getString(R.string.no), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, + int whichButton) { + } + }); + AlertDialog alert = ask_alrt.create(); + alert.show(); + } else + install(apk); + } + + public void removeSummaryHeader() { + Fragment summary = getChildFragmentManager().findFragmentByTag(SUMMARY_TAG); + if (summary != null) { + getChildFragmentManager().beginTransaction().remove(summary).commit(); + headerView.removeAllViews(); + headerView.setVisibility(View.GONE); + summaryFragment = null; + } + } + + public void setupSummaryHeader() { + Fragment fragment = getChildFragmentManager().findFragmentByTag(SUMMARY_TAG); + if (fragment != null) { + summaryFragment = (AppDetailsSummaryFragment)fragment; + } else { + summaryFragment = new AppDetailsSummaryFragment(); + } + getChildFragmentManager().beginTransaction().replace(headerView.getId(), summaryFragment, SUMMARY_TAG).commit(); + headerView.setVisibility(View.VISIBLE); + } + } + +} \ No newline at end of file diff --git a/src/org/fdroid/fdroid/Preferences.java b/src/org/fdroid/fdroid/Preferences.java index ca6d750fd..4254617a7 100644 --- a/src/org/fdroid/fdroid/Preferences.java +++ b/src/org/fdroid/fdroid/Preferences.java @@ -1,13 +1,19 @@ package org.fdroid.fdroid; -import java.util.*; - import android.content.Context; import android.content.SharedPreferences; import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + /** * Handles shared preferences for FDroid, looking after the names of * preferences, default values and caching. Needs to be setup in the FDroidApp @@ -55,6 +61,9 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi private static final boolean DEFAULT_SYSTEM_INSTALLER = false; private static final boolean DEFAULT_LOCAL_REPO_BONJOUR = true; private static final boolean DEFAULT_LOCAL_REPO_HTTPS = false; + private static final boolean DEFAULT_INCOMP_VER = false; + private static final boolean DEFAULT_EXPERT = false; + private static final boolean DEFAULT_PERMISSIONS = false; private boolean compactLayout = DEFAULT_COMPACT_LAYOUT; private boolean filterAppsRequiringRoot = DEFAULT_ROOTED; @@ -92,6 +101,18 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi return preferences.getBoolean(PREF_LOCAL_REPO_BONJOUR, DEFAULT_LOCAL_REPO_BONJOUR); } + public boolean showIncompatibleVersions() { + return preferences.getBoolean(PREF_INCOMP_VER, DEFAULT_INCOMP_VER); + } + + public boolean showPermissions() { + return preferences.getBoolean(PREF_PERMISSIONS, DEFAULT_PERMISSIONS); + } + + public boolean expertMode() { + return preferences.getBoolean(PREF_EXPERT, DEFAULT_EXPERT); + } + public boolean isLocalRepoHttpsEnabled() { return preferences.getBoolean(PREF_LOCAL_REPO_HTTPS, DEFAULT_LOCAL_REPO_HTTPS); } diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java index cd1322f10..ea97f786d 100644 --- a/src/org/fdroid/fdroid/Utils.java +++ b/src/org/fdroid/fdroid/Utils.java @@ -23,24 +23,41 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetManager; import android.content.res.XmlResourceParser; import android.net.Uri; +import android.text.Editable; +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; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Formatter; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; public final class Utils { @@ -452,4 +469,38 @@ public final class Utils { return String.format("%0" + (bytes.length << 1) + "X", bi); } + + // Need this to add the unimplemented support for ordered and unordered + // lists to Html.fromHtml(). + public static class HtmlTagHandler implements Html.TagHandler { + int listNum; + + @Override + public void handleTag(boolean opening, String tag, Editable output, + XMLReader reader) { + if (tag.equals("ul")) { + if (opening) + listNum = -1; + else + output.append('\n'); + } else if (opening && tag.equals("ol")) { + if (opening) + listNum = 1; + else + output.append('\n'); + } else if (tag.equals("li")) { + if (opening) { + if (listNum == -1) { + output.append("\t• "); + } else { + output.append("\t").append(Integer.toString(listNum)).append(". "); + listNum++; + } + } else { + output.append('\n'); + } + } + } + } + } From 208146882e7a988683a82245e7fb99f33649a620 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Wed, 4 Jun 2014 17:38:42 +0930 Subject: [PATCH 7/8] Fixed light theme on API > 11 Forgot that it also needed to go in the values-v11 styles file. --- res/values-v11/styles.xml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml index 6a337d6e4..f78bab7d6 100644 --- a/res/values-v11/styles.xml +++ b/res/values-v11/styles.xml @@ -2,10 +2,14 @@ + - + + + From 81cbef82b4e58b115363c5c2bfb207a3ba9dabc2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 4 Jun 2014 23:12:49 -0400 Subject: [PATCH 8/8] finish ant/gradle build for appcompat-v7 Both preferencefragment and appcompat-v7 submodules need the android-support-v4.jar to be included in their respective libs/ dirs in order for ant to build those projects. --- ant-prepare.sh | 4 ++++ build.gradle | 9 +++++++++ custom_rules.xml | 10 ++++++++++ project.properties | 2 ++ 4 files changed, 25 insertions(+) diff --git a/ant-prepare.sh b/ant-prepare.sh index 13929a08c..7f41a1347 100755 --- a/ant-prepare.sh +++ b/ant-prepare.sh @@ -1,10 +1,14 @@ #!/bin/bash -ex +target=`sed -n 's,^target=\(.*\),\1,p' project.properties` + android update lib-project --path extern/UniversalImageLoader/library android update lib-project --path extern/AndroidPinning android update lib-project --path extern/MemorizingTrustManager android update lib-project --path extern/libsuperuser/libsuperuser android update lib-project --path extern/zxing-core +android update lib-project --path extern/android-support-v4-preferencefragment +android update lib-project --path extern/Support/v7/appcompat --target $target android update project --path . --name F-Droid { echo -e "\nSuccessfully updated the main project.\n"; } 2>/dev/null diff --git a/build.gradle b/build.gradle index 18ba173d6..d9699d94e 100644 --- a/build.gradle +++ b/build.gradle @@ -125,6 +125,15 @@ android.applicationVariants.all { variant -> // This is the hacky way which we force the subprojects to use the same build tools: // http://stackoverflow.com/a/21032272 subprojects { + + // The support-v4 library assumes certain things are defined in the + // root project (which is usually the android-support project, but + // this time it is the F-Droid project. + if (project.name.equals("support-v4")) { + apply plugin: 'maven' + rootProject.ext.supportRepoOut = "" + } + afterEvaluate { android { diff --git a/custom_rules.xml b/custom_rules.xml index 7747834aa..5cb033506 100644 --- a/custom_rules.xml +++ b/custom_rules.xml @@ -1,6 +1,16 @@ + + + + + + + +