Merge branch 'appcompat-on-top-of-pserwylo' into 'master'

Appcompat on top of pserwylo

This is a refactor of @pserwylo's !19 to get appcompat building with `ant` and Eclipse.  I reviewed @pserwylo's commits and they are ready to go as they are in this merge request.
This commit is contained in:
Peter Serwylo 2014-06-05 05:38:53 +00:00
commit 5af5306311
35 changed files with 1210 additions and 999 deletions

4
.gitmodules vendored
View File

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

View File

@ -7,7 +7,7 @@
android:versionName="0.69-test" >
<uses-sdk
android:minSdkVersion="5"
android:minSdkVersion="7"
android:targetSdkVersion="19" />
<supports-screens
@ -203,6 +203,9 @@
android:launchMode="singleTop"
android:parentActivityName=".FDroid"
android:screenOrientation="portrait" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:name=".views.SelectLocalAppsActivity"
@ -216,7 +219,11 @@
android:name=".views.RepoDetailsActivity"
android:label="@string/menu_manage"
android:parentActivityName=".ManageRepo"
android:windowSoftInputMode="stateHidden" />
android:windowSoftInputMode="stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ManageRepo" />
</activity>
<activity
android:name=".AppDetails"

View File

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

View File

@ -7,16 +7,18 @@ buildscript {
}
}
def toolVersion = "19.1"
apply plugin: 'android'
sdkLoc = System.getenv("ANDROID_HOME")
def toolVersion = "19.1"
sdkLoc = getSdkPath()
FileCollection getAndroidPrebuilt(String apiLevel) {
files("$sdkLoc/platforms/android-$apiLevel/android.jar")
}
dependencies {
compile project(':support-v4')
compile project(':support-appcompat-v7')
compile project(':extern:AndroidPinning')
compile project(':extern:UniversalImageLoader:library')
compile project(':extern:MemorizingTrustManager')
@ -25,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') {
@ -120,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 {
@ -138,3 +152,17 @@ 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
}

View File

@ -1,6 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
<target name="-pre-build">
<echo message="Copying a bunch of files around to make F-Droid and its submodules build" />
<copy tofile="extern/android-support-v4-preferencefragment/libs/android-support-v4.jar"
file="libs/android-support-v4.jar" />
<copy tofile="extern/Support/v7/appcompat/libs/android-support-v4.jar"
file="libs/android-support-v4.jar" />
</target>
<target name="javadoc" description="Generate Javadocs">
<javadoc sourcepath="${source.dir}"
destdir="${out.dir}/javadoc"

@ -0,0 +1 @@
Subproject commit fab3cf8a0699a5ba45b66d4dcddcf7947239de9b

View File

@ -7,6 +7,8 @@ android.library.reference.2=extern/MemorizingTrustManager
android.library.reference.3=extern/AndroidPinning
android.library.reference.4=extern/libsuperuser/libsuperuser
android.library.reference.5=extern/zxing-core
android.library.reference.6=extern/android-support-v4-preferencefragment
android.library.reference.7=extern/Support/v7/appcompat
# For java libraries, place symlinks in extern/symlinks-for-ant-and-eclipse/
source.dir=src;extern/symlinks-for-ant-and-eclipse

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
android:baselineAligned="false"
android:orientation="horizontal">
<ScrollView
android:id="@+id/app_summary_container"
android:layout_width="0px"
android:layout_weight="0.5"
android:layout_height="wrap_content">
<fragment
android:id="@+id/fragment_app_summary"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:name="org.fdroid.fdroid.AppDetails$AppDetailsSummaryFragment"
tools:layout="@layout/app_details_summary"/>
</ScrollView>
<fragment
android:id="@+id/fragment_app_list"
android:layout_width="0px"
android:layout_weight="0.5"
android:layout_height="wrap_content"
android:name="org.fdroid.fdroid.AppDetails$AppDetailsListFragment"
tools:layout="@android:layout/list_content"/>
</LinearLayout>

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselineAligned="false"
android:orientation="horizontal" >
<ScrollView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="0.5" >
<LinearLayout
android:id="@+id/landleft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="false"
android:textSize="18sp"
android:textStyle="bold" />
<RelativeLayout
android:id="@+id/header"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:padding="4dp"
android:scaleType="fitCenter"
/>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4dp"
android:layout_toRightOf="@id/icon"
android:layout_toEndOf="@id/icon"
android:orientation="vertical" >
<TextView
android:id="@+id/license"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:textSize="13sp" />
<TextView
android:id="@+id/categories"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/license"
android:layout_above="@id/status"
android:layout_centerVertical="true"
android:textSize="13sp" />
<TextView
android:id="@+id/status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:textSize="13sp" />
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
</ScrollView>
<ListView
android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="0.5"
android:scrollbars="none" />
</LinearLayout>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<fragment
android:id="@+id/fragment_app_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:name="org.fdroid.fdroid.AppDetails$AppDetailsListFragment"
tools:layout="@android:layout/list_content"/>
</RelativeLayout>

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="8dp"
android:baselineAligned="false"
android:orientation="vertical" >
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:padding="4dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
tools:src="@drawable/ic_launcher"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/icon"
android:layout_toEndOf="@id/icon"
android:layout_alignBottom="@id/icon"
android:paddingLeft="4dp"
tools:text="Installed"
android:textSize="13sp" android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="false"
tools:text="F-Droid"
android:textSize="18sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:layout_toRightOf="@id/icon"
android:layout_above="@id/status" />
<TextView
android:id="@+id/categories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignBottom="@id/icon"
android:paddingLeft="4dp"
tools:text="System, Internet"
android:textSize="13sp" android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/license"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_above="@id/categories"
android:paddingRight="4dp"
tools:text="GPLv3+"
android:textSize="13sp" />
<TextView
android:id="@+id/summary"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/icon"
android:textStyle="bold"
tools:text="Application manager" />
<TextView
android:id="@+id/appid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/summary"
android:textSize="12sp"
tools:text="org.fdroid.fdroid" />
<TextView
android:id="@+id/signature"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/appid"
android:textSize="12sp" />
<TextView
android:id="@+id/antifeatures"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/signature"
android:layout_marginTop="6sp"
android:textStyle="bold"
android:textColor="#ff0000"
tools:text="Feeds you too much chocolate" />
<TextView
android:id="@+id/description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/antifeatures"
android:layout_marginTop="8sp"
android:textSize="13sp"
android:singleLine="false"
tools:text="Connects to F-Droid compatible repositories. The default repo is hosted at f-droid.org, which contains only bona fide FOSS.
Android is open in the sense that you are free to install apks from anywhere you wish, but there are many good reasons for using a client/repository setup:
* Be notified when updates are available
* Keep track of older and beta versions
* Filter apps that aren't compatible with the device
* Find apps via categories and searchable descriptions
* Access associated urls for donations, source code etc.
* Stay safe by checking repo index signatures and apk hashes
Changelog" />
<TextView
android:id="@+id/permissions"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/description"
android:layout_marginTop="8sp"
android:singleLine="true"
android:textStyle="bold"
tools:text="Permissions for version 1.0" />
<TextView
android:id="@+id/permissions_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/permissions"
android:textSize="13sp"
android:singleLine="false"
tools:text=" * Full network access
* View network connections
* View Wi-Fi connections
* Connect and disconnect from Wi-Fi
* Pair with Bluetooth devices
* Run at startup
* Modify or delete the contents of your USB storage
* Control Near Field Communication
* Directly install apps
* Delete apps
* Full permission to all device features and storage
* Test access to protected storage" />
</RelativeLayout>

View File

@ -1,104 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
android:baselineAligned="false"
android:orientation="vertical" >
<RelativeLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="false" >
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_centerVertical="true"
android:padding="4dp"
android:scaleType="fitCenter"
/>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/icon"
android:layout_toEndOf="@id/icon"
android:padding="5dp"
android:baselineAligned="false"
android:orientation="vertical"
>
<TextView
android:id="@+id/license"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:singleLine="true"
android:ellipsize="end"
android:layout_marginLeft="6sp"
android:layout_marginStart="6sp"
android:textSize="12sp"
/>
<TextView
android:id="@+id/title"
android:textSize="17sp"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:gravity="start"
android:textAlignment="viewStart"
android:layout_toLeftOf="@id/license"
android:layout_toStartOf="@id/license" />
<TextView
android:id="@+id/categories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="6sp"
android:layout_marginStart="6sp"
android:layout_below="@id/title"
android:textSize="12sp" />
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:textSize="12sp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:gravity="start"
android:textAlignment="viewStart"
android:layout_toLeftOf="@id/categories"
android:layout_toStartOf="@id/categories"
android:layout_below="@id/title" />
</RelativeLayout>
</RelativeLayout>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none" />
</LinearLayout>

View File

@ -1,11 +1,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppBaseThemeDark" parent="android:Theme.Holo">
<style name="AppBaseThemeDark" parent="Theme.AppCompat">
<!-- API 11 theme customizations go here -->
</style>
</style>
<style name="AppBaseThemeLight" parent="android:Theme.Holo.Light">
<style name="AppBaseThemeLight" parent="Theme.AppCompat.Light">
<!-- API 11 theme customizations go here -->
</style>
</style>
<style name="AppBaseThemeLightWithDarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- API 11 theme customizations go here -->
</style>
</resources>

View File

@ -11,5 +11,6 @@
<string-array name="themeNames">
<item>Dark</item>
<item>Light</item>
<item>Light (with dark action bar)</item>
</string-array>
</resources>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="categorySpinner" />
<item type="id" name="appDetailsSummaryHeader" />
</resources>

View File

@ -25,6 +25,7 @@
<string-array name="themeValues">
<item>dark</item>
<item>light</item>
<item>lightWithDarkActionBar</item>
</string-array>
</resources>

View File

@ -1,10 +1,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppBaseThemeDark" parent="android:Theme.Black">
<style name="AppBaseThemeDark" parent="Theme.AppCompat">
<!-- backward-compatibility theme options go here -->
</style>
<style name="AppBaseThemeLight" parent="android:Theme.Light">
<style name="AppBaseThemeLight" parent="Theme.AppCompat.Light">
<!-- backward-compatibility theme options go here -->
</style>
<style name="AppBaseThemeLightWithDarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- backward-compatibility theme options go here -->
</style>
@ -25,4 +29,8 @@
<!-- customizations that are not API-level specific go here. -->
</style>
<style name="AppThemeLightWithDarkActionBar" parent="AppBaseThemeLightWithDarkActionBar">
<!-- customizations that are not API-level specific go here. -->
</style>
</resources>

View File

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

View File

@ -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 <em>not</em> 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<Apk> {
class ApkListAdapter extends ArrayAdapter<Apk> {
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<Apk> 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<String> 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<String> 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);
}
}
}

View File

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

View File

@ -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;
@ -74,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;
@ -88,11 +90,14 @@ public class FDroidApp extends Application {
public void applyTheme(Activity activity) {
switch (curTheme) {
case dark:
//activity.setTheme(R.style.AppThemeDark);
return;
activity.setTheme(R.style.AppThemeDark);
break;
case light:
activity.setTheme(R.style.AppThemeLight);
return;
break;
case lightWithDarkActionBar:
activity.setTheme(R.style.AppThemeLightWithDarkActionBar);
break;
}
}

View File

@ -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
@ -53,9 +52,9 @@ public class ManageRepo extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
@ -72,7 +71,7 @@ public class ManageRepo extends FragmentActivity {
.commit();
}
ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override

View File

@ -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
@ -35,7 +36,10 @@ public class NfcNotEnabledActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
final Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= 16) {
doOnJellybean(intent);

View File

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

View File

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

View File

@ -20,17 +20,16 @@ package org.fdroid.fdroid;
import android.content.Intent;
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.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 FragmentActivity {
public class SearchResults extends ActionBarActivity {
private static final int SEARCH = Menu.FIRST;
@ -38,7 +37,6 @@ public class SearchResults extends FragmentActivity {
public void onCreate(Bundle savedInstanceState) {
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
// Start a search by just typing
@ -61,7 +59,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);
}

View File

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

View File

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

View File

@ -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;
@ -46,8 +60,8 @@ public class LocalRepoActivity extends Activity {
/** 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);

View File

@ -1,27 +1,31 @@
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) {
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);

View File

@ -1,37 +1,40 @@
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;
@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);

View File

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

View File

@ -1,29 +1,30 @@
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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.select_local_apps_activity);
}
@ -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;
}

View File

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

View File

@ -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<Cursor>, OnQueryTextListener {
implements LoaderManager.LoaderCallbacks<Cursor>, 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);