Initial files
This commit is contained in:
commit
0b71cb7e73
7
.classpath
Normal file
7
.classpath
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="gen"/>
|
||||||
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
local.properties
|
||||||
|
bin/*
|
||||||
|
gen/*
|
||||||
|
|
33
.project
Normal file
33
.project
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>fdroid</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
22
AndroidManifest.xml
Normal file
22
AndroidManifest.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.fdroid.fdroid" android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
<application android:label="@string/app_name">
|
||||||
|
<activity android:name="FDroid">
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:icon="@drawable/icon">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name="ManageRepo" />
|
||||||
|
<activity android:name="Settings" />
|
||||||
|
<activity android:name="AppDetails" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
</manifest>
|
17
build.properties
Normal file
17
build.properties
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# This file is used to override default values used by the Ant build system.
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems, as it is
|
||||||
|
# integral to the build system of your project.
|
||||||
|
|
||||||
|
# This file is only used by the Ant script.
|
||||||
|
|
||||||
|
# You can use this to override default values such as
|
||||||
|
# 'source.dir' for the location of your java source folder and
|
||||||
|
# 'out.dir' for the location of your output folder.
|
||||||
|
|
||||||
|
# You can also use it define how the release builds are signed by declaring
|
||||||
|
# the following properties:
|
||||||
|
# 'key.store' for the location of your keystore and
|
||||||
|
# 'key.alias' for the name of the key to use.
|
||||||
|
# The password will be asked during the build when you use the 'release' target.
|
||||||
|
|
67
build.xml
Normal file
67
build.xml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="FDroid" default="help">
|
||||||
|
|
||||||
|
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||||
|
It contains the path to the SDK. It should *NOT* be checked in in Version
|
||||||
|
Control Systems. -->
|
||||||
|
<property file="local.properties" />
|
||||||
|
|
||||||
|
<!-- The build.properties file can be created by you and is never touched
|
||||||
|
by the 'android' tool. This is the place to change some of the default property values
|
||||||
|
used by the Ant rules.
|
||||||
|
Here are some properties you may want to change/update:
|
||||||
|
|
||||||
|
application.package
|
||||||
|
the name of your application package as defined in the manifest. Used by the
|
||||||
|
'uninstall' rule.
|
||||||
|
source.dir
|
||||||
|
the name of the source directory. Default is 'src'.
|
||||||
|
out.dir
|
||||||
|
the name of the output directory. Default is 'bin'.
|
||||||
|
|
||||||
|
Properties related to the SDK location or the project target should be updated
|
||||||
|
using the 'android' tool with the 'update' action.
|
||||||
|
|
||||||
|
This file is an integral part of the build system for your application and
|
||||||
|
should be checked in in Version Control Systems.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<property file="build.properties" />
|
||||||
|
|
||||||
|
<!-- The default.properties file is created and updated by the 'android' tool, as well
|
||||||
|
as ADT.
|
||||||
|
This file is an integral part of the build system for your application and
|
||||||
|
should be checked in in Version Control Systems. -->
|
||||||
|
<property file="default.properties" />
|
||||||
|
|
||||||
|
<!-- Custom Android task to deal with the project target, and import the proper rules.
|
||||||
|
This requires ant 1.6.0 or above. -->
|
||||||
|
<path id="android.antlibs">
|
||||||
|
<pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
|
||||||
|
<pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
|
||||||
|
<pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
|
||||||
|
<pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
|
||||||
|
<pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<taskdef name="setup"
|
||||||
|
classname="com.android.ant.SetupTask"
|
||||||
|
classpathref="android.antlibs" />
|
||||||
|
|
||||||
|
<!-- Execute the Android Setup task that will setup some properties specific to the target,
|
||||||
|
and import the build rules files.
|
||||||
|
|
||||||
|
The rules file is imported from
|
||||||
|
<SDK>/platforms/<target_platform>/templates/android_rules.xml
|
||||||
|
|
||||||
|
To customize some build steps for your project:
|
||||||
|
- copy the content of the main node <project> from android_rules.xml
|
||||||
|
- paste it in this build.xml below the <setup /> task.
|
||||||
|
- disable the import by changing the setup task below to <setup import="false" />
|
||||||
|
|
||||||
|
This will ensure that the properties are setup correctly but that your customized
|
||||||
|
build steps are used.
|
||||||
|
-->
|
||||||
|
<setup />
|
||||||
|
|
||||||
|
</project>
|
13
default.properties
Normal file
13
default.properties
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system use,
|
||||||
|
# "build.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
|
||||||
|
# Indicates whether an apk should be generated for each density.
|
||||||
|
split.density=false
|
||||||
|
# Project target.
|
||||||
|
target=android-4
|
BIN
res/drawable/btn_check_off.png
Normal file
BIN
res/drawable/btn_check_off.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable/btn_check_on.png
Normal file
BIN
res/drawable/btn_check_on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable/icon.png
Normal file
BIN
res/drawable/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
38
res/layout/about.xml
Normal file
38
res/layout/about.xml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?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:orientation="vertical" android:paddingLeft="5px">
|
||||||
|
|
||||||
|
<LinearLayout android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent" android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/site" android:text="@string/about_site"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
|
android:textSize="16px" android:textColor="#ffffff" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/sitec" android:text="http://f-droid.org"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/not" android:text=" "
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/mail" android:text="@string/about_mail"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
|
android:textSize="16px" android:autoLink="email" android:textColor="#ffffff" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/mailc" android:text="admin@f-droid.org"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/about11" android:text="@string/about_desc"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||||
|
android:textSize="16px" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
37
res/layout/addrepo.xml
Normal file
37
res/layout/addrepo.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:ems="20"
|
||||||
|
android:layout_height="wrap_content" android:text="@string/repo_add_url"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/edit_uri"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:ems="20"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="http://"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
20
res/layout/apklistitem.xml
Normal file
20
res/layout/apklistitem.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/vw1" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:orientation="horizontal">
|
||||||
|
|
||||||
|
<RelativeLayout android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/version" android:textStyle="bold"
|
||||||
|
android:singleLine="true" android:ellipsize="marquee"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/status" android:textSize="12sp"
|
||||||
|
android:layout_below="@id/version" android:layout_alignParentRight="false"
|
||||||
|
android:layout_height="wrap_content" android:layout_width="wrap_content" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
41
res/layout/appdetails.xml
Normal file
41
res/layout/appdetails.xml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?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:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/header"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/icon" android:cropToPadding="true"
|
||||||
|
android:padding="4px" android:scaleType="centerInside"
|
||||||
|
android:layout_height="50px" android:layout_width="50px" />
|
||||||
|
|
||||||
|
<RelativeLayout android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/title" android:textStyle="bold"
|
||||||
|
android:singleLine="true" android:ellipsize="marquee"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/license" android:textSize="12sp"
|
||||||
|
android:layout_below="@id/title" android:layout_alignParentRight="true"
|
||||||
|
android:layout_height="wrap_content" android:layout_width="wrap_content" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/status" android:layout_toLeftOf="@id/license"
|
||||||
|
android:layout_below="@id/title" android:layout_alignParentLeft="true"
|
||||||
|
android:textSize="12sp" android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/description" android:singleLine="false"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<ListView android:id="@android:id/list" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
39
res/layout/applistitem.xml
Normal file
39
res/layout/applistitem.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/vw1" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/vw2" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/icon" android:cropToPadding="true"
|
||||||
|
android:padding="4px" android:scaleType="centerInside"
|
||||||
|
android:layout_height="50px" android:layout_width="50px" />
|
||||||
|
|
||||||
|
<RelativeLayout android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/name" android:textStyle="bold"
|
||||||
|
android:singleLine="true" android:ellipsize="marquee"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/license" android:textSize="12sp"
|
||||||
|
android:layout_below="@id/name" android:layout_alignParentRight="true"
|
||||||
|
android:layout_height="wrap_content" android:layout_width="wrap_content" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/status" android:layout_toLeftOf="@id/license"
|
||||||
|
android:layout_below="@id/name" android:layout_alignParentLeft="true"
|
||||||
|
android:textSize="12sp" android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" />
|
||||||
|
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/summary" android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
16
res/layout/fdroid.xml
Normal file
16
res/layout/fdroid.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/tabhost" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<TabWidget android:id="@android:id/tabs"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<FrameLayout android:id="@android:id/tabcontent"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||||
|
android:paddingTop="65px">
|
||||||
|
<ListView android:id="@android:id/list" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</TabHost>
|
36
res/layout/remrepo.xml
Normal file
36
res/layout/remrepo.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="6dip"
|
||||||
|
android:paddingRight="6dip"
|
||||||
|
android:paddingBottom="6dip">
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/rem_lst"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawSelectorOnTop="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
32
res/layout/repolist.xml
Normal file
32
res/layout/repolist.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<ListView android:id="@android:id/list"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<TextView android:id="@android:id/empty"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="200px"
|
||||||
|
android:text="@string/no_repo"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
46
res/layout/repolisticons.xml
Normal file
46
res/layout/repolisticons.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/vw1"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/img"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/uri"
|
||||||
|
android:textSize="21sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
37
res/values/alertstr.xml
Normal file
37
res/values/alertstr.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="up_server">Servers: </string>
|
||||||
|
<string name="lstver">Server version: </string>
|
||||||
|
<string name="isinst">Installed: </string>
|
||||||
|
<string name="instver">Installed version: </string>
|
||||||
|
<string name="install">Install </string>
|
||||||
|
<string name="rem">Uninstall </string>
|
||||||
|
<string name="update">Update!</string>
|
||||||
|
|
||||||
|
<string name="update_alrt">There updates available for some installed applications.\nDo you wish to see them?</string>
|
||||||
|
<string name="repo_alrt">The list of repositories in use has been changed.\nDo you wish to update them?</string>
|
||||||
|
|
||||||
|
<string name="error_download_alrt">Could not connect to server or apk file is corrupt!</string>
|
||||||
|
<string name="download_alrt">Getting application from:\n </string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</resources>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
37
res/values/menu.xml
Normal file
37
res/values/menu.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<string name="menu_update_repo">Update</string>
|
||||||
|
<string name="menu_manage">Manage Repos</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="menu_about">About</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<string name="menu_add_repo">New Repository</string>
|
||||||
|
<string name="menu_rem_repo">Remove Repository</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</resources>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
23
res/values/path.xml
Normal file
23
res/values/path.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="icons_path">/sdcard/.fdroid/icons/</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto
|
||||||
|
* roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
-->
|
61
res/values/strings.xml
Normal file
61
res/values/strings.xml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">FDroid</string>
|
||||||
|
<string name="about_title">About FDroid</string>
|
||||||
|
<string name="about_desc">Based on Aptoide.\nReleased under the GNU GPL v2 license.</string>
|
||||||
|
<string name="about_site">Home: </string>
|
||||||
|
<string name="about_mail">e-Mail: </string>
|
||||||
|
<string name="about_website">Web Site</string>
|
||||||
|
|
||||||
|
<string name="no_found">No application found!</string>
|
||||||
|
<string name="no_repo">You have no repositories configured!\n\nA repository is a source of applications. To add one, press the MENU button now and enter the URL.\n\nA repository URL looks something like this: http://f-droid.org/repo</string>
|
||||||
|
|
||||||
|
<string name="not_inst">Not Installed</string>
|
||||||
|
<string name="installed">Installed - Ver.:</string>
|
||||||
|
<string name="installed_update">Update possible - Ver.:</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<string name="error">Error</string>
|
||||||
|
<string name="ok">Ok</string>
|
||||||
|
|
||||||
|
<string name="yes">Yes</string>
|
||||||
|
<string name="no">No</string>
|
||||||
|
<string name="repo_add_title">Add new repository</string>
|
||||||
|
<string name="repo_add_add">Add</string>
|
||||||
|
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="repo_delete_title">Chose repository to remove</string>
|
||||||
|
<string name="url_website">http://f-droid.org</string>
|
||||||
|
<string name="server_connection_error">Could not connect to server!</string>
|
||||||
|
|
||||||
|
<string name="repo_update_title">Update repositories</string>
|
||||||
|
<string name="tab_installed">Installed</string>
|
||||||
|
<string name="tab_noninstalled">Available</string>
|
||||||
|
<string name="tab_updates">Updates</string>
|
||||||
|
<string name="update_available">Updates available</string>
|
||||||
|
<string name="process_wait_title">Please Wait</string>
|
||||||
|
<string name="process_update_msg">Updating application list...</string>
|
||||||
|
<string name="connection_error">Could not connect to the network.</string>
|
||||||
|
<string name="connection_timeout">Timeout</string>
|
||||||
|
<string name="connection_error_msg">Could not connect to server!</string>
|
||||||
|
<string name="download">Download</string>
|
||||||
|
<string name="download_server">Getting application from</string>
|
||||||
|
<string name="apk_market_view">Market</string>
|
||||||
|
<string name="apk_version_new"> available v </string>
|
||||||
|
<string name="settings_sort_title">Sort application list by:</string>
|
||||||
|
<string name="settings_sort_abc">Alphabetically</string>
|
||||||
|
<string name="settings_sort_installed">Installed / Not Installed</string>
|
||||||
|
<string name="settings_sort_recent">Most recent first</string>
|
||||||
|
<string name="settings_sort_rating">Rating</string>
|
||||||
|
<string name="settings_filter_title">Show applications:</string>
|
||||||
|
<string name="settings_filter_category">By category</string>
|
||||||
|
<string name="settings_filter_all">All applications</string>
|
||||||
|
<string name="settings_save">Save</string>
|
||||||
|
<string name="repo_add_url">Repository URL</string>
|
||||||
|
</resources>
|
354
src/org/fdroid/fdroid/AppDetails.java
Normal file
354
src/org/fdroid/fdroid/AppDetails.java
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
|
||||||
|
public class AppDetails extends ListActivity {
|
||||||
|
|
||||||
|
private String LOCAL_PATH = "/sdcard/.fdroid";
|
||||||
|
|
||||||
|
private static final int REQUEST_INSTALL = 0;
|
||||||
|
private static final int REQUEST_UNINSTALL = 1;
|
||||||
|
|
||||||
|
private class ApkListAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
private List<DB.Apk> items = new ArrayList<DB.Apk>();
|
||||||
|
|
||||||
|
public ApkListAdapter(Context context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(DB.Apk apk) {
|
||||||
|
items.add(apk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return items.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
View v = convertView;
|
||||||
|
if (v == null) {
|
||||||
|
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
v = vi.inflate(R.layout.apklistitem, null);
|
||||||
|
}
|
||||||
|
DB.Apk apk = items.get(position);
|
||||||
|
TextView version = (TextView) v.findViewById(R.id.version);
|
||||||
|
version.setText("Version " + apk.version);
|
||||||
|
TextView status = (TextView) v.findViewById(R.id.status);
|
||||||
|
if (apk.version.equals(app.installedVersion))
|
||||||
|
status.setText("Installed");
|
||||||
|
else
|
||||||
|
status.setText("Not installed");
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DB db;
|
||||||
|
private DB.App app;
|
||||||
|
private DB.Apk curapk;
|
||||||
|
private String appid;
|
||||||
|
private PackageManager mPm;
|
||||||
|
private ProgressDialog pd;
|
||||||
|
|
||||||
|
private Context mctx = this;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.appdetails);
|
||||||
|
|
||||||
|
db = new DB(this);
|
||||||
|
mPm = getPackageManager();
|
||||||
|
|
||||||
|
Intent i = getIntent();
|
||||||
|
appid = "";
|
||||||
|
if (!i.hasExtra("appid")) {
|
||||||
|
Log.d("FDroid", "No application ID in AppDetails!?");
|
||||||
|
} else {
|
||||||
|
appid = i.getStringExtra("appid");
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the display and list contents. Used when entering the activity, and
|
||||||
|
// also when something has been installed/uninstalled. In the latter case,
|
||||||
|
// 'update' is true to make the installed status get refreshed.
|
||||||
|
private void reset(boolean update) {
|
||||||
|
|
||||||
|
Log.d("FDroid", "Getting application details for " + appid);
|
||||||
|
app = db.getApps(appid, null, update).get(0);
|
||||||
|
|
||||||
|
// Set the icon...
|
||||||
|
ImageView iv = (ImageView) findViewById(R.id.icon);
|
||||||
|
String icon_path = this.getString(R.string.icons_path) + app.icon;
|
||||||
|
File test_icon = new File(icon_path);
|
||||||
|
if (test_icon.exists()) {
|
||||||
|
iv.setImageDrawable(new BitmapDrawable(icon_path));
|
||||||
|
} else {
|
||||||
|
iv.setImageResource(android.R.drawable.sym_def_app_icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
tv = (TextView) findViewById(R.id.status);
|
||||||
|
int vnum = app.apks.size();
|
||||||
|
String v = vnum == 1 ? "version" : "versions";
|
||||||
|
tv.setText("" + vnum + " " + v + ", "
|
||||||
|
+ (app.installedVersion == null ? "not" : "1") + " installed");
|
||||||
|
tv = (TextView) findViewById(R.id.description);
|
||||||
|
tv.setText(app.description);
|
||||||
|
|
||||||
|
// Set up the list...
|
||||||
|
ApkListAdapter la = new ApkListAdapter(this);
|
||||||
|
for (DB.Apk apk : app.apks)
|
||||||
|
la.addItem(apk);
|
||||||
|
setListAdapter(la);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
// Create alert dialog...
|
||||||
|
final AlertDialog p = new AlertDialog.Builder(this).create();
|
||||||
|
|
||||||
|
curapk = app.apks.get(position);
|
||||||
|
|
||||||
|
// Set the title and icon...
|
||||||
|
String icon_path = this.getString(R.string.icons_path) + app.icon;
|
||||||
|
File test_icon = new File(icon_path);
|
||||||
|
if (test_icon.exists()) {
|
||||||
|
p.setIcon(new BitmapDrawable(icon_path));
|
||||||
|
} else {
|
||||||
|
p.setIcon(android.R.drawable.sym_def_app_icon);
|
||||||
|
}
|
||||||
|
p.setTitle(app.name + " " + curapk.version);
|
||||||
|
|
||||||
|
boolean caninstall = true;
|
||||||
|
String installed = getString(R.string.no);
|
||||||
|
if (app.installedVersion != null) {
|
||||||
|
if (app.installedVersion.equals(curapk.version)) {
|
||||||
|
installed = getString(R.string.yes);
|
||||||
|
caninstall = false;
|
||||||
|
} else {
|
||||||
|
installed += " - " + app.installedVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.setMessage(getString(R.string.isinst) + " " + installed);
|
||||||
|
|
||||||
|
p.setButton(getString(R.string.ok),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (caninstall) {
|
||||||
|
p.setButton2(getString(R.string.install),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
p.dismiss();
|
||||||
|
new Thread() {
|
||||||
|
public void run() {
|
||||||
|
String apk_pkg = downloadFile(app, curapk);
|
||||||
|
if (apk_pkg == null) {
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.arg1 = 1;
|
||||||
|
download_handler.sendMessage(msg);
|
||||||
|
download_error_handler
|
||||||
|
.sendEmptyMessage(0);
|
||||||
|
} else {
|
||||||
|
installApk(apk_pkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
p.setButton3(getString(R.string.apk_market_view),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
startActivity(new Intent(Intent.ACTION_VIEW, Uri
|
||||||
|
.parse("market://search?q=pname:" + app.id)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!caninstall) {
|
||||||
|
p.setButton2(getString(R.string.rem),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
removeApk(app.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
p.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the requested apk file, given the DB.App and DB.Apk
|
||||||
|
// that refer to it. Returns the path to the downloaded file, or
|
||||||
|
// null if the download was not successful.
|
||||||
|
private String downloadFile(DB.App app, DB.Apk apk) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String apkname = apk.apkName;
|
||||||
|
String localfile = new String(LOCAL_PATH + "/" + apkname);
|
||||||
|
String remotefile = apk.server + "/" + apkname.replace(" ", "%20");
|
||||||
|
|
||||||
|
Log.d("FDroid", "Downloading apk from " + remotefile);
|
||||||
|
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.arg1 = 0;
|
||||||
|
msg.obj = new String(remotefile);
|
||||||
|
download_handler.sendMessage(msg);
|
||||||
|
|
||||||
|
BufferedInputStream getit = new BufferedInputStream(new URL(
|
||||||
|
remotefile).openStream());
|
||||||
|
|
||||||
|
FileOutputStream saveit = new FileOutputStream(localfile);
|
||||||
|
BufferedOutputStream bout = new BufferedOutputStream(saveit, 1024);
|
||||||
|
byte data[] = new byte[1024];
|
||||||
|
|
||||||
|
int readed = getit.read(data, 0, 1024);
|
||||||
|
while (readed != -1) {
|
||||||
|
bout.write(data, 0, readed);
|
||||||
|
readed = getit.read(data, 0, 1024);
|
||||||
|
}
|
||||||
|
bout.close();
|
||||||
|
getit.close();
|
||||||
|
saveit.close();
|
||||||
|
File f = new File(localfile);
|
||||||
|
Md5Handler hash = new Md5Handler();
|
||||||
|
|
||||||
|
if (apk.hash.equalsIgnoreCase(hash.md5Calc(f))) {
|
||||||
|
return localfile;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("FDroid", "Download failed - " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Handler download_handler = new Handler() {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
if (msg.arg1 == 0) {
|
||||||
|
pd = ProgressDialog.show(mctx, getString(R.string.download),
|
||||||
|
getString(R.string.download_server) + ":\n "
|
||||||
|
+ msg.obj.toString(), true);
|
||||||
|
} else {
|
||||||
|
pd.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private Handler download_error_handler = new Handler() {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
Toast.makeText(mctx, getString(R.string.connection_error_msg),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void removeApk(String id) {
|
||||||
|
PackageInfo pkginfo;
|
||||||
|
try {
|
||||||
|
pkginfo = mPm.getPackageInfo(id, 0);
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
Log.d("FDroid", "Couldn't find package " + id + " to uninstall.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Uri uri = Uri.fromParts("package", pkginfo.packageName, null);
|
||||||
|
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
|
||||||
|
startActivityForResult(intent, REQUEST_UNINSTALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installApk(String id) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(android.content.Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(Uri.parse("file://" + id),
|
||||||
|
"application/vnd.android.package-archive");
|
||||||
|
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.arg1 = 1;
|
||||||
|
download_handler.sendMessage(msg);
|
||||||
|
|
||||||
|
startActivityForResult(intent, REQUEST_INSTALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
reset(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
445
src/org/fdroid/fdroid/DB.java
Normal file
445
src/org/fdroid/fdroid/DB.java
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
/*
|
||||||
|
a * Copyright (C) 2010 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class DB {
|
||||||
|
|
||||||
|
private static final String DATABASE_NAME = "fdroid_db";
|
||||||
|
|
||||||
|
private SQLiteDatabase db;
|
||||||
|
|
||||||
|
// The TABLE_APP table stores details of all the applications we know about.
|
||||||
|
// This information is retrieved from the repositories.
|
||||||
|
private static final String TABLE_APP = "fdroid_app";
|
||||||
|
private static final String CREATE_TABLE_APP = "create table " + TABLE_APP
|
||||||
|
+ "( " + "id text not null, " + "name text not null, "
|
||||||
|
+ "summary text not null, " + "icon text, "
|
||||||
|
+ "description text not null, " + "license text not null, "
|
||||||
|
+ "webURL text, " + "trackerURL text, " + "sourceURL text, "
|
||||||
|
+ "installedVersion text," + "hasUpdates int not null,"
|
||||||
|
+ "primary key(id));";
|
||||||
|
|
||||||
|
public static class App {
|
||||||
|
|
||||||
|
public App() {
|
||||||
|
name = "Unknown";
|
||||||
|
summary = "Unknown application";
|
||||||
|
icon = "noicon.png";
|
||||||
|
id = "unknown";
|
||||||
|
hasUpdates = false;
|
||||||
|
updated = false;
|
||||||
|
apks = new Vector<Apk>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id;
|
||||||
|
public String name;
|
||||||
|
public String summary;
|
||||||
|
public String icon;
|
||||||
|
public String description;
|
||||||
|
public String license;
|
||||||
|
public String webURL;
|
||||||
|
public String trackerURL;
|
||||||
|
public String sourceURL;
|
||||||
|
public String installedVersion;
|
||||||
|
|
||||||
|
// True if there are new versions (apks) that the user hasn't
|
||||||
|
// explicitly ignored.
|
||||||
|
public boolean hasUpdates;
|
||||||
|
|
||||||
|
// Used internally for tracking during repo updates.
|
||||||
|
public boolean updated;
|
||||||
|
|
||||||
|
public Vector<Apk> apks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TABLE_APK table stores details of all the application versions we
|
||||||
|
// know
|
||||||
|
// about. Each relates directly back to an entry in TABLE_APP.
|
||||||
|
// This information is retrieved from the repositories.
|
||||||
|
private static final String TABLE_APK = "fdroid_apk";
|
||||||
|
private static final String CREATE_TABLE_APK = "create table " + TABLE_APK
|
||||||
|
+ "( " + "id text not null, " + "version text not null, "
|
||||||
|
+ "server text not null, " + "hash text not null, "
|
||||||
|
+ "apkName text not null, " + "primary key(id,version));";
|
||||||
|
|
||||||
|
public static class Apk {
|
||||||
|
|
||||||
|
public Apk() {
|
||||||
|
updated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id;
|
||||||
|
public String version;
|
||||||
|
public String server;
|
||||||
|
public String hash;
|
||||||
|
public String apkName;
|
||||||
|
|
||||||
|
// Used internally for tracking during repo updates.
|
||||||
|
public boolean updated;
|
||||||
|
|
||||||
|
public String getURL() {
|
||||||
|
String path = apkName.replace(" ", "%20");
|
||||||
|
return server + "/" + path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TABLE_REPO table stores the details of the repositories in use.
|
||||||
|
private static final String TABLE_REPO = "fdroid_repo";
|
||||||
|
private static final String CREATE_TABLE_REPO = "create table "
|
||||||
|
+ TABLE_REPO + " (" + "address text primary key, "
|
||||||
|
+ "inuse integer not null, " + "priority integer not null);";
|
||||||
|
|
||||||
|
public static class Repo {
|
||||||
|
public String address;
|
||||||
|
public boolean inuse;
|
||||||
|
public int priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PackageManager mPm;
|
||||||
|
|
||||||
|
public DB(Context ctx) {
|
||||||
|
db = ctx.openOrCreateDatabase(DATABASE_NAME, 0, null);
|
||||||
|
|
||||||
|
Cursor c = db.rawQuery(
|
||||||
|
"SELECT name FROM sqlite_master WHERE type='table' AND name= '"
|
||||||
|
+ TABLE_REPO + "'", null);
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
c.close();
|
||||||
|
|
||||||
|
mPm = ctx.getPackageManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the database, i.e. (re-)create all the tables from scratch and
|
||||||
|
// populate any initial data.
|
||||||
|
public void reset() {
|
||||||
|
db.execSQL("drop table if exists " + TABLE_REPO);
|
||||||
|
db.execSQL("drop table if exists " + TABLE_APP);
|
||||||
|
db.execSQL("drop table if exists " + TABLE_APK);
|
||||||
|
db.execSQL(CREATE_TABLE_REPO);
|
||||||
|
db.execSQL(CREATE_TABLE_APP);
|
||||||
|
db.execSQL(CREATE_TABLE_APK);
|
||||||
|
addServer("http://f-droid.org/repo", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
db.close();
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a list of apps matching the given criteria.
|
||||||
|
// 'appid' - specific app id to retrieve, or null
|
||||||
|
// 'filter' - search text to filter on.
|
||||||
|
// 'update' - update installed version information from device, rather than
|
||||||
|
// simply using values cached in the database. Slower.
|
||||||
|
public Vector<App> getApps(String appid, String filter, boolean update) {
|
||||||
|
Vector<App> result = new Vector<App>();
|
||||||
|
Cursor c = null;
|
||||||
|
Cursor c2 = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
String query = "select * from " + TABLE_APP;
|
||||||
|
if (appid != null) {
|
||||||
|
query += " where id = '" + appid + "'";
|
||||||
|
} else if (filter != null) {
|
||||||
|
query += " where name like '%" + filter + "%'"
|
||||||
|
+ " or description like '%" + filter + "%'";
|
||||||
|
}
|
||||||
|
query += " order by name collate nocase";
|
||||||
|
|
||||||
|
c = db.rawQuery(query, null);
|
||||||
|
c.moveToFirst();
|
||||||
|
while (!c.isAfterLast()) {
|
||||||
|
|
||||||
|
App app = new App();
|
||||||
|
app.id = c.getString(c.getColumnIndex("id"));
|
||||||
|
app.name = c.getString(c.getColumnIndex("name"));
|
||||||
|
app.summary = c.getString(c.getColumnIndex("summary"));
|
||||||
|
app.icon = c.getString(c.getColumnIndex("icon"));
|
||||||
|
app.description = c.getString(c.getColumnIndex("description"));
|
||||||
|
app.license = c.getString(c.getColumnIndex("license"));
|
||||||
|
app.webURL = c.getString(c.getColumnIndex("webURL"));
|
||||||
|
app.trackerURL = c.getString(c.getColumnIndex("trackerURL"));
|
||||||
|
app.sourceURL = c.getString(c.getColumnIndex("sourceURL"));
|
||||||
|
app.installedVersion = c.getString(c
|
||||||
|
.getColumnIndex("installedVersion"));
|
||||||
|
app.hasUpdates = c.getInt(c.getColumnIndex("hasUpdates")) == 1;
|
||||||
|
|
||||||
|
c2 = db.rawQuery("select * from " + TABLE_APK + " where "
|
||||||
|
+ "id = '" + app.id + "'", null);
|
||||||
|
c2.moveToFirst();
|
||||||
|
while (!c2.isAfterLast()) {
|
||||||
|
Apk apk = new Apk();
|
||||||
|
apk.id = app.id;
|
||||||
|
apk.version = c2.getString(c2.getColumnIndex("version"));
|
||||||
|
apk.server = c2.getString(c2.getColumnIndex("server"));
|
||||||
|
apk.hash = c2.getString(c2.getColumnIndex("hash"));
|
||||||
|
apk.apkName = c2.getString(c2.getColumnIndex("apkName"));
|
||||||
|
app.apks.add(apk);
|
||||||
|
c2.moveToNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(app);
|
||||||
|
c.moveToNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("FDroid", "Exception during database reading - "
|
||||||
|
+ e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (c != null) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
if (c2 != null) {
|
||||||
|
c2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
getUpdates(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify installed status against the system's package list.
|
||||||
|
private void getUpdates(Vector<DB.App> apps) {
|
||||||
|
List<PackageInfo> installedPackages = mPm.getInstalledPackages(0);
|
||||||
|
Map<String, PackageInfo> systemApks = new HashMap<String, PackageInfo>();
|
||||||
|
for (PackageInfo appInfo : installedPackages) {
|
||||||
|
systemApks.put(appInfo.packageName, appInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DB.App app : apps) {
|
||||||
|
if (systemApks.containsKey(app.id)) {
|
||||||
|
String version = systemApks.get(app.id).versionName;
|
||||||
|
if (app.installedVersion == null
|
||||||
|
|| !app.installedVersion.equals(version)) {
|
||||||
|
setInstalledVersion(app.id, version);
|
||||||
|
app.installedVersion = version;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (app.installedVersion != null) {
|
||||||
|
setInstalledVersion(app.id, null);
|
||||||
|
app.installedVersion = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector<App> updateApps = null;
|
||||||
|
|
||||||
|
// Called before a repo update starts.
|
||||||
|
public void beginUpdate() {
|
||||||
|
updateApps = getApps(null, null, true);
|
||||||
|
Log.d("FDroid", "AppUpdate: " + updateApps.size()
|
||||||
|
+ " apps before starting.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a repo update ends. Any applications that have not been
|
||||||
|
// updated (by a call to updateApplication) are assumed to be no longer
|
||||||
|
// in the repos.
|
||||||
|
public void endUpdate() {
|
||||||
|
for (App app : updateApps) {
|
||||||
|
if (!app.updated) {
|
||||||
|
// The application hasn't been updated, so it's no longer
|
||||||
|
// in the repos.
|
||||||
|
db.delete(TABLE_APP, "id = '" + app.id + "'", null);
|
||||||
|
} else {
|
||||||
|
for (Apk apk : app.apks) {
|
||||||
|
if (!apk.updated) {
|
||||||
|
// The package hasn't been updated, so this is a
|
||||||
|
// version that's no longer available.
|
||||||
|
db.delete(TABLE_APK, "id = '" + app.id
|
||||||
|
+ " and version ='" + apk.version + "'", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateApps = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called during update to supply new details for an application (or
|
||||||
|
// details of a completely new one).
|
||||||
|
public void updateApplication(App upapp) {
|
||||||
|
|
||||||
|
if (updateApps == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
for (App app : updateApps) {
|
||||||
|
if (app.id.equals(upapp.id)) {
|
||||||
|
Log.d("FDroid", "AppUpdate: " + app.id
|
||||||
|
+ " is already in the database.");
|
||||||
|
updateAppIfDifferent(app, upapp);
|
||||||
|
app.updated = true;
|
||||||
|
found = true;
|
||||||
|
for (Apk upapk : upapp.apks) {
|
||||||
|
boolean afound = false;
|
||||||
|
for (Apk apk : app.apks) {
|
||||||
|
if (apk.version.equals(upapk.version)) {
|
||||||
|
Log.d("FDroid", "AppUpdate: " + apk.version
|
||||||
|
+ " is a known version.");
|
||||||
|
updateApkIfDifferent(apk, upapk);
|
||||||
|
apk.updated = true;
|
||||||
|
afound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!afound) {
|
||||||
|
// A new version of this application.
|
||||||
|
Log.d("FDroid", "AppUpdate: " + upapk.version
|
||||||
|
+ " is a new version.");
|
||||||
|
updateApkIfDifferent(null, upapk);
|
||||||
|
upapk.updated = true;
|
||||||
|
app.apks.add(upapk);
|
||||||
|
app.hasUpdates = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
// It's a brand new application...
|
||||||
|
Log
|
||||||
|
.d("FDroid", "AppUpdate: " + upapp.id
|
||||||
|
+ " is a new application.");
|
||||||
|
updateAppIfDifferent(null, upapp);
|
||||||
|
for (Apk upapk : upapp.apks) {
|
||||||
|
updateApkIfDifferent(null, upapk);
|
||||||
|
}
|
||||||
|
upapp.updated = true;
|
||||||
|
updateApps.add(upapp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update application details in the database, if different to the
|
||||||
|
// previous ones.
|
||||||
|
// 'oldapp' - previous details - i.e. what's in the database.
|
||||||
|
// If null, this app is not in the database at all and
|
||||||
|
// should be added.
|
||||||
|
// 'upapp' - updated details
|
||||||
|
private void updateAppIfDifferent(App oldapp, App upapp) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("id", upapp.id);
|
||||||
|
values.put("name", upapp.name);
|
||||||
|
values.put("summary", upapp.summary);
|
||||||
|
values.put("icon", upapp.icon);
|
||||||
|
values.put("description", upapp.description);
|
||||||
|
values.put("license", upapp.license);
|
||||||
|
values.put("webURL", upapp.webURL);
|
||||||
|
values.put("trackerURL", upapp.trackerURL);
|
||||||
|
values.put("sourceURL", upapp.sourceURL);
|
||||||
|
values.put("installedVersion", upapp.installedVersion);
|
||||||
|
values.put("hasUpdates", upapp.hasUpdates ? 1 : 0);
|
||||||
|
if (oldapp != null) {
|
||||||
|
db.update(TABLE_APP, values, "id = '" + oldapp.id + "'", null);
|
||||||
|
} else {
|
||||||
|
db.insert(TABLE_APP, null, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update apk details in the database, if different to the
|
||||||
|
// previous ones.
|
||||||
|
// 'oldapk' - previous details - i.e. what's in the database.
|
||||||
|
// If null, this apk is not in the database at all and
|
||||||
|
// should be added.
|
||||||
|
// 'upapk' - updated details
|
||||||
|
private void updateApkIfDifferent(Apk oldapk, Apk upapk) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("id", upapk.id);
|
||||||
|
values.put("version", upapk.version);
|
||||||
|
values.put("server", upapk.server);
|
||||||
|
values.put("hash", upapk.hash);
|
||||||
|
values.put("apkName", upapk.apkName);
|
||||||
|
if (oldapk != null) {
|
||||||
|
db.update(TABLE_APK, values, "id = '" + oldapk.id
|
||||||
|
+ "' and version = '" + oldapk.version + "'", null);
|
||||||
|
} else {
|
||||||
|
db.insert(TABLE_APK, null, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstalledVersion(String id, String version) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("installedVersion", version);
|
||||||
|
db.update(TABLE_APP, values, "id = '" + id + "'", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of the configured repositories.
|
||||||
|
public Vector<Repo> getRepos() {
|
||||||
|
Vector<Repo> repos = new Vector<Repo>();
|
||||||
|
Cursor c = null;
|
||||||
|
try {
|
||||||
|
c = db.rawQuery("select address, inuse, priority from "
|
||||||
|
+ TABLE_REPO + " order by priority", null);
|
||||||
|
c.moveToFirst();
|
||||||
|
while(!c.isAfterLast()) {
|
||||||
|
Repo repo = new Repo();
|
||||||
|
repo.address = c.getString(0);
|
||||||
|
repo.inuse = (c.getInt(1) == 1);
|
||||||
|
repo.priority = c.getInt(2);
|
||||||
|
repos.add(repo);
|
||||||
|
c.moveToNext();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
} finally {
|
||||||
|
if (c != null) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeServerStatus(String address) {
|
||||||
|
db.rawQuery("update " + TABLE_REPO
|
||||||
|
+ " set inuse=1-inuse where address='" + address + "'", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addServer(String address, int priority) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("address", address);
|
||||||
|
values.put("inuse", 1);
|
||||||
|
values.put("priority", priority);
|
||||||
|
db.insert(TABLE_REPO, null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeServers(Vector<String> addresses) {
|
||||||
|
for (String address : addresses) {
|
||||||
|
db.delete(TABLE_REPO, "address = '" + address + "'", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
538
src/org/fdroid/fdroid/FDroid.java
Normal file
538
src/org/fdroid/fdroid/FDroid.java
Normal file
@ -0,0 +1,538 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.SAXParser;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
|
||||||
|
import android.R.drawable;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.app.TabActivity;
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TabHost;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
import android.widget.TabHost.TabSpec;
|
||||||
|
|
||||||
|
public class FDroid extends TabActivity implements OnItemClickListener {
|
||||||
|
|
||||||
|
private class AppListAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
private List<DB.App> items = new ArrayList<DB.App>();
|
||||||
|
|
||||||
|
public AppListAdapter(Context context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(DB.App app) {
|
||||||
|
items.add(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
items.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return items.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
View v = convertView;
|
||||||
|
if (v == null) {
|
||||||
|
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
v = vi.inflate(R.layout.applistitem, null);
|
||||||
|
}
|
||||||
|
DB.App app = items.get(position);
|
||||||
|
|
||||||
|
TextView name = (TextView) v.findViewById(R.id.name);
|
||||||
|
name.setText(app.name);
|
||||||
|
|
||||||
|
String vs = " versions available";
|
||||||
|
int numav = app.apks.size();
|
||||||
|
if (numav == 1)
|
||||||
|
vs = " version available";
|
||||||
|
TextView status = (TextView) v.findViewById(R.id.status);
|
||||||
|
status.setText(numav + vs);
|
||||||
|
|
||||||
|
TextView license = (TextView) v.findViewById(R.id.license);
|
||||||
|
license.setText(app.license);
|
||||||
|
|
||||||
|
TextView summary = (TextView) v.findViewById(R.id.summary);
|
||||||
|
summary.setText(app.summary);
|
||||||
|
|
||||||
|
ImageView icon = (ImageView) v.findViewById(R.id.icon);
|
||||||
|
String iconpath = new String(FDroid.this
|
||||||
|
.getString(R.string.icons_path)
|
||||||
|
+ app.icon);
|
||||||
|
File icn = new File(iconpath);
|
||||||
|
if (icn.exists() && icn.length() > 0) {
|
||||||
|
new Uri.Builder().build();
|
||||||
|
icon.setImageURI(Uri.parse(iconpath));
|
||||||
|
} else {
|
||||||
|
icon.setImageResource(android.R.drawable.sym_def_app_icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String LOCAL_PATH = "/sdcard/.fdroid";
|
||||||
|
private String XML_PATH = LOCAL_PATH + "/remapklst.xml";
|
||||||
|
|
||||||
|
private static final int REQUEST_APPDETAILS = 0;
|
||||||
|
private static final int REQUEST_MANAGEREPOS = 1;
|
||||||
|
|
||||||
|
private static final int UPDATE_REPO = Menu.FIRST;
|
||||||
|
private static final int MANAGE_REPO = Menu.FIRST + 1;
|
||||||
|
private static final int RESET_DB = Menu.FIRST + 2;
|
||||||
|
private static final int ABOUT = Menu.FIRST + 3;
|
||||||
|
|
||||||
|
private DB db = null;
|
||||||
|
|
||||||
|
// Apps that are available to be installed
|
||||||
|
private AppListAdapter apps_av = new AppListAdapter(this);
|
||||||
|
|
||||||
|
// Apps that are installed
|
||||||
|
private AppListAdapter apps_in = new AppListAdapter(this);
|
||||||
|
|
||||||
|
// Apps that can be upgraded
|
||||||
|
private AppListAdapter apps_up = new AppListAdapter(this);
|
||||||
|
|
||||||
|
private ProgressDialog pd;
|
||||||
|
|
||||||
|
private Context mctx = this;
|
||||||
|
|
||||||
|
private static final String TAB_IN = "INST";
|
||||||
|
private static final String TAB_UN = "UNIN";
|
||||||
|
private static final String TAB_UP = "UPDT";
|
||||||
|
private TabHost tabHost;
|
||||||
|
private TabSpec ts;
|
||||||
|
private TabSpec ts1;
|
||||||
|
private TabSpec tsUp;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.fdroid);
|
||||||
|
|
||||||
|
File local_path = new File(LOCAL_PATH);
|
||||||
|
if (!local_path.exists())
|
||||||
|
local_path.mkdir();
|
||||||
|
|
||||||
|
File icon_path = new File(this.getString(R.string.icons_path));
|
||||||
|
if (!icon_path.exists())
|
||||||
|
icon_path.mkdir();
|
||||||
|
|
||||||
|
db = new DB(this);
|
||||||
|
|
||||||
|
tabHost = getTabHost();
|
||||||
|
createTabs();
|
||||||
|
|
||||||
|
Intent i = getIntent();
|
||||||
|
if (i.hasExtra("uri")) {
|
||||||
|
Intent call = new Intent(this, ManageRepo.class);
|
||||||
|
call.putExtra("uri", i.getStringExtra("uri"));
|
||||||
|
startActivityForResult(call, REQUEST_MANAGEREPOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
populateLists(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
menu.add(Menu.NONE, UPDATE_REPO, 1, R.string.menu_update_repo).setIcon(
|
||||||
|
android.R.drawable.ic_menu_rotate);
|
||||||
|
menu.add(Menu.NONE, MANAGE_REPO, 2, R.string.menu_manage).setIcon(
|
||||||
|
android.R.drawable.ic_menu_agenda);
|
||||||
|
menu.add(Menu.NONE, RESET_DB, 4, "Reset DB").setIcon(
|
||||||
|
android.R.drawable.ic_menu_revert);
|
||||||
|
menu.add(Menu.NONE, ABOUT, 5, R.string.menu_about).setIcon(
|
||||||
|
android.R.drawable.ic_menu_help);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
|
||||||
|
case UPDATE_REPO:
|
||||||
|
updateRepos();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MANAGE_REPO:
|
||||||
|
Intent i = new Intent(this, ManageRepo.class);
|
||||||
|
startActivityForResult(i, REQUEST_MANAGEREPOS);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case RESET_DB:
|
||||||
|
db.reset();
|
||||||
|
populateLists(true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case ABOUT:
|
||||||
|
LayoutInflater li = LayoutInflater.from(this);
|
||||||
|
View view = li.inflate(R.layout.about, null);
|
||||||
|
Builder p = new AlertDialog.Builder(this).setView(view);
|
||||||
|
final AlertDialog alrt = p.create();
|
||||||
|
alrt.setIcon(R.drawable.icon);
|
||||||
|
alrt.setTitle(getString(R.string.about_title));
|
||||||
|
alrt.setButton(AlertDialog.BUTTON_NEUTRAL,
|
||||||
|
getString(R.string.about_website),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton) {
|
||||||
|
Uri uri = Uri
|
||||||
|
.parse(getString(R.string.url_website));
|
||||||
|
startActivity(new Intent(Intent.ACTION_VIEW, uri));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alrt.setButton(AlertDialog.BUTTON_NEGATIVE, "Ok",
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alrt.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_APPDETAILS:
|
||||||
|
populateLists(false);
|
||||||
|
break;
|
||||||
|
case REQUEST_MANAGEREPOS:
|
||||||
|
if (data.hasExtra("update")) {
|
||||||
|
AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
|
||||||
|
ask_alrt.setTitle(getString(R.string.repo_update_title));
|
||||||
|
ask_alrt.setIcon(android.R.drawable.ic_menu_rotate);
|
||||||
|
ask_alrt.setMessage(getString(R.string.repo_alrt));
|
||||||
|
ask_alrt.setPositiveButton(getString(R.string.yes),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton) {
|
||||||
|
updateRepos();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ask_alrt.setNegativeButton(getString(R.string.no),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AlertDialog alert = ask_alrt.create();
|
||||||
|
alert.show();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTabs() {
|
||||||
|
tabHost.clearAllTabs();
|
||||||
|
|
||||||
|
// TabContentFactory that can generate the appropriate list for each
|
||||||
|
// tab...
|
||||||
|
TabHost.TabContentFactory tf = new TabHost.TabContentFactory() {
|
||||||
|
@Override
|
||||||
|
public View createTabContent(String tag) {
|
||||||
|
|
||||||
|
AppListAdapter ad;
|
||||||
|
if (tag.equals(TAB_IN))
|
||||||
|
ad = apps_in;
|
||||||
|
else if (tag.equals(TAB_UP))
|
||||||
|
ad = apps_up;
|
||||||
|
else
|
||||||
|
ad = apps_av;
|
||||||
|
|
||||||
|
ListView lst = new ListView(FDroid.this);
|
||||||
|
lst.setOnItemClickListener(FDroid.this);
|
||||||
|
lst.setAdapter(ad);
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the tab of installed apps...
|
||||||
|
ts = tabHost.newTabSpec(TAB_IN);
|
||||||
|
ts.setIndicator(getString(R.string.tab_installed), getResources()
|
||||||
|
.getDrawable(drawable.star_off));
|
||||||
|
ts.setContent(tf);
|
||||||
|
|
||||||
|
// Create the tab of apps with updates...
|
||||||
|
tsUp = tabHost.newTabSpec(TAB_UP);
|
||||||
|
tsUp.setIndicator(getString(R.string.tab_updates), getResources()
|
||||||
|
.getDrawable(drawable.star_on));
|
||||||
|
tsUp.setContent(tf);
|
||||||
|
|
||||||
|
// Create the tab of available apps...
|
||||||
|
ts1 = tabHost.newTabSpec(TAB_UN);
|
||||||
|
ts1.setIndicator(getString(R.string.tab_noninstalled), getResources()
|
||||||
|
.getDrawable(drawable.ic_input_add));
|
||||||
|
ts1.setContent(tf);
|
||||||
|
|
||||||
|
tabHost.addTab(ts1);
|
||||||
|
tabHost.addTab(ts);
|
||||||
|
tabHost.addTab(tsUp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the lists.
|
||||||
|
// 'update' - true to update the installed status of the applications
|
||||||
|
// by asking the system.
|
||||||
|
private void populateLists(boolean update) {
|
||||||
|
|
||||||
|
Vector<DB.App> apps = db.getApps(null, null, update);
|
||||||
|
Log.d("FDroid", "Updating lists - " + apps.size() + " apps in total");
|
||||||
|
|
||||||
|
apps_in.clear();
|
||||||
|
apps_av.clear();
|
||||||
|
apps_up.clear();
|
||||||
|
for (DB.App app : apps) {
|
||||||
|
if (app.installedVersion == null) {
|
||||||
|
apps_av.addItem(app);
|
||||||
|
} else {
|
||||||
|
apps_in.addItem(app);
|
||||||
|
if (app.hasUpdates)
|
||||||
|
apps_up.addItem(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the lists that the data behind the adapter has changed, so
|
||||||
|
// they can refresh...
|
||||||
|
apps_av.notifyDataSetChanged();
|
||||||
|
apps_in.notifyDataSetChanged();
|
||||||
|
apps_up.notifyDataSetChanged();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateRepos() {
|
||||||
|
pd = ProgressDialog.show(this, getString(R.string.process_wait_title),
|
||||||
|
getString(R.string.process_update_msg), true);
|
||||||
|
pd.setIcon(android.R.drawable.ic_dialog_info);
|
||||||
|
|
||||||
|
// Check for connection first!
|
||||||
|
ConnectivityManager netstate = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
if (netstate.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED
|
||||||
|
|| netstate.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED) {
|
||||||
|
new Thread() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
db.beginUpdate();
|
||||||
|
Vector<DB.Repo> repos = db.getRepos();
|
||||||
|
for (DB.Repo repo : repos) {
|
||||||
|
if (repo.inuse) {
|
||||||
|
downloadRepoIndex(repo.address);
|
||||||
|
xmlPass(repo.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.endUpdate();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("FDroid", "Exception while updating - "
|
||||||
|
+ e.getMessage());
|
||||||
|
}
|
||||||
|
update_handler.sendEmptyMessage(0);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
pd.dismiss();
|
||||||
|
Toast.makeText(FDroid.this, getString(R.string.connection_error),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass XML info to BD a xml file must exists...
|
||||||
|
*/
|
||||||
|
private void xmlPass(String srv) {
|
||||||
|
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||||
|
try {
|
||||||
|
SAXParser sp = spf.newSAXParser();
|
||||||
|
XMLReader xr = sp.getXMLReader();
|
||||||
|
RepoXMLHandler handler = new RepoXMLHandler(this, srv);
|
||||||
|
xr.setContentHandler(handler);
|
||||||
|
|
||||||
|
InputStreamReader isr = new FileReader(new File(XML_PATH));
|
||||||
|
InputSource is = new InputSource(isr);
|
||||||
|
xr.parse(is);
|
||||||
|
File xml_file = new File(XML_PATH);
|
||||||
|
xml_file.delete();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (SAXException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download a repo index to a temporary file on the SD card.
|
||||||
|
private void downloadRepoIndex(String srv) {
|
||||||
|
try {
|
||||||
|
BufferedInputStream getit = new BufferedInputStream(new URL(srv
|
||||||
|
+ "/index.xml").openStream());
|
||||||
|
|
||||||
|
File file_teste = new File(XML_PATH);
|
||||||
|
if (file_teste.exists())
|
||||||
|
file_teste.delete();
|
||||||
|
|
||||||
|
FileOutputStream saveit = new FileOutputStream(XML_PATH);
|
||||||
|
BufferedOutputStream bout = new BufferedOutputStream(saveit, 1024);
|
||||||
|
byte data[] = new byte[1024];
|
||||||
|
|
||||||
|
int readed = getit.read(data, 0, 1024);
|
||||||
|
while (readed != -1) {
|
||||||
|
bout.write(data, 0, readed);
|
||||||
|
readed = getit.read(data, 0, 1024);
|
||||||
|
}
|
||||||
|
bout.close();
|
||||||
|
getit.close();
|
||||||
|
saveit.close();
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.obj = new String(srv);
|
||||||
|
error_handler.sendMessage(msg);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handlers for thread functions that need to access GUI
|
||||||
|
*/
|
||||||
|
private Handler update_handler = new Handler() {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
populateLists(true);
|
||||||
|
if (pd.isShowing())
|
||||||
|
pd.dismiss();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private Handler error_handler = new Handler() {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
if (pd.isShowing())
|
||||||
|
pd.dismiss();
|
||||||
|
AlertDialog p = new AlertDialog.Builder(mctx).create();
|
||||||
|
p.setTitle(getString(R.string.connection_timeout));
|
||||||
|
p.setIcon(android.R.drawable.ic_dialog_alert);
|
||||||
|
p.setMessage(getString(R.string.connection_error_msg) + ": < "
|
||||||
|
+ msg.obj.toString() + " >");
|
||||||
|
p.setButton(getString(R.string.ok),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
p.show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler for a click on one of the items in an application list. Pops
|
||||||
|
// up a dialog that shows the details of the application and all its
|
||||||
|
// available versions, with buttons to allow installation etc.
|
||||||
|
public void onItemClick(AdapterView<?> arg0, View arg1, final int arg2,
|
||||||
|
long arg3) {
|
||||||
|
|
||||||
|
final DB.App app;
|
||||||
|
String curtab = tabHost.getCurrentTabTag();
|
||||||
|
if (curtab.equalsIgnoreCase(TAB_IN)) {
|
||||||
|
app = (DB.App) apps_in.getItem(arg2);
|
||||||
|
} else if (curtab.equalsIgnoreCase(TAB_UP)) {
|
||||||
|
app = (DB.App) apps_up.getItem(arg2);
|
||||||
|
} else {
|
||||||
|
app = (DB.App) apps_av.getItem(arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, AppDetails.class);
|
||||||
|
intent.putExtra("appid", app.id);
|
||||||
|
startActivityForResult(intent, REQUEST_APPDETAILS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
205
src/org/fdroid/fdroid/ManageRepo.java
Normal file
205
src/org/fdroid/fdroid/ManageRepo.java
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
|
||||||
|
* Copyright (C) 2010 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.SimpleAdapter;
|
||||||
|
|
||||||
|
public class ManageRepo extends ListActivity {
|
||||||
|
|
||||||
|
private DB db = null;
|
||||||
|
|
||||||
|
private final int ADD_REPO = 1;
|
||||||
|
private final int REM_REPO = 2;
|
||||||
|
|
||||||
|
private boolean changed = false;
|
||||||
|
|
||||||
|
private Vector<DB.Repo> repos;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.repolist);
|
||||||
|
|
||||||
|
db = new DB(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
|
||||||
|
super.onResume();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void redraw() {
|
||||||
|
repos = db.getRepos();
|
||||||
|
|
||||||
|
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
|
||||||
|
Map<String, Object> server_line;
|
||||||
|
|
||||||
|
for (DB.Repo repo : repos) {
|
||||||
|
server_line = new HashMap<String, Object>();
|
||||||
|
server_line.put("address", repo.address);
|
||||||
|
if (repo.inuse) {
|
||||||
|
server_line.put("inuse", R.drawable.btn_check_on);
|
||||||
|
} else {
|
||||||
|
server_line.put("inuse", R.drawable.btn_check_off);
|
||||||
|
}
|
||||||
|
result.add(server_line);
|
||||||
|
}
|
||||||
|
SimpleAdapter show_out = new SimpleAdapter(this, result,
|
||||||
|
R.layout.repolisticons, new String[] { "address", "inuse" },
|
||||||
|
new int[] { R.id.uri, R.id.img });
|
||||||
|
|
||||||
|
setListAdapter(show_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
|
||||||
|
super.onListItemClick(l, v, position, id);
|
||||||
|
db.changeServerStatus(repos.get(position).address);
|
||||||
|
changed = true;
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
menu.add(Menu.NONE, ADD_REPO, 1, R.string.menu_add_repo).setIcon(
|
||||||
|
android.R.drawable.ic_menu_add);
|
||||||
|
menu.add(Menu.NONE, REM_REPO, 2, R.string.menu_rem_repo).setIcon(
|
||||||
|
android.R.drawable.ic_menu_close_clear_cancel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||||
|
|
||||||
|
super.onMenuItemSelected(featureId, item);
|
||||||
|
LayoutInflater li = LayoutInflater.from(this);
|
||||||
|
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case ADD_REPO:
|
||||||
|
View view = li.inflate(R.layout.addrepo, null);
|
||||||
|
Builder p = new AlertDialog.Builder(this).setView(view);
|
||||||
|
final AlertDialog alrt = p.create();
|
||||||
|
|
||||||
|
alrt.setIcon(android.R.drawable.ic_menu_add);
|
||||||
|
alrt.setTitle(getString(R.string.repo_add_title));
|
||||||
|
alrt.setButton(getString(R.string.repo_add_add),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
EditText uri = (EditText) alrt
|
||||||
|
.findViewById(R.id.edit_uri);
|
||||||
|
String uri_str = uri.getText().toString();
|
||||||
|
db.addServer(uri_str, 10);
|
||||||
|
changed = true;
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
alrt.setButton2(getString(R.string.cancel),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alrt.show();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case REM_REPO:
|
||||||
|
final Vector<String> rem_lst = new Vector<String>();
|
||||||
|
CharSequence[] b = new CharSequence[repos.size()];
|
||||||
|
for (int i = 0; i < repos.size(); i++) {
|
||||||
|
b[i] = repos.get(i).address;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setTitle(getString(R.string.repo_delete_title));
|
||||||
|
builder.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
|
||||||
|
builder.setMultiChoiceItems(b, null,
|
||||||
|
new DialogInterface.OnMultiChoiceClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton, boolean isChecked) {
|
||||||
|
if (isChecked) {
|
||||||
|
rem_lst
|
||||||
|
.addElement(repos.get(whichButton).address);
|
||||||
|
} else {
|
||||||
|
rem_lst
|
||||||
|
.removeElement(repos.get(whichButton).address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setPositiveButton(getString(R.string.ok),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton) {
|
||||||
|
db.removeServers(rem_lst);
|
||||||
|
changed = true;
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(getString(R.string.cancel),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int whichButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AlertDialog alert = builder.create();
|
||||||
|
alert.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
Intent ret = new Intent();
|
||||||
|
if (changed)
|
||||||
|
ret.putExtra("update", true);
|
||||||
|
this.setResult(RESULT_OK, ret);
|
||||||
|
db.close();
|
||||||
|
super.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
src/org/fdroid/fdroid/Md5Handler.java
Normal file
41
src/org/fdroid/fdroid/Md5Handler.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public class Md5Handler {
|
||||||
|
|
||||||
|
private MessageDigest digest;
|
||||||
|
|
||||||
|
public Md5Handler() {
|
||||||
|
try {
|
||||||
|
digest = MessageDigest.getInstance("MD5");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String md5Calc(File f) {
|
||||||
|
String md5hash = null;
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int read = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream is = new FileInputStream(f);
|
||||||
|
while ((read = is.read(buffer)) > 0) {
|
||||||
|
digest.update(buffer, 0, read);
|
||||||
|
}
|
||||||
|
byte[] md5sum = digest.digest();
|
||||||
|
BigInteger bigInt = new BigInteger(1, md5sum);
|
||||||
|
md5hash = bigInt.toString(16);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
164
src/org/fdroid/fdroid/RepoXMLHandler.java
Normal file
164
src/org/fdroid/fdroid/RepoXMLHandler.java
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
|
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class RepoXMLHandler extends DefaultHandler {
|
||||||
|
|
||||||
|
Context mctx;
|
||||||
|
String mserver;
|
||||||
|
|
||||||
|
private DB db = null;
|
||||||
|
|
||||||
|
private DB.App curapp = null;
|
||||||
|
private DB.Apk curapk = null;
|
||||||
|
private String curel = null;
|
||||||
|
|
||||||
|
public RepoXMLHandler(Context ctx, String srv) {
|
||||||
|
mctx = ctx;
|
||||||
|
mserver = srv;
|
||||||
|
db = new DB(mctx);
|
||||||
|
db.beginUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endDocument() throws SAXException {
|
||||||
|
super.endDocument();
|
||||||
|
db.endUpdate();
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length)
|
||||||
|
throws SAXException {
|
||||||
|
|
||||||
|
super.characters(ch, start, length);
|
||||||
|
|
||||||
|
String str = new String(ch).substring(start, start + length);
|
||||||
|
if (curapk != null && curel != null) {
|
||||||
|
if (curel == "version")
|
||||||
|
curapk.version = str;
|
||||||
|
else if (curel == "hash")
|
||||||
|
curapk.hash = str;
|
||||||
|
else if (curel == "apkname")
|
||||||
|
curapk.apkName = str;
|
||||||
|
} else if (curapp != null && curel != null) {
|
||||||
|
if (curel == "id")
|
||||||
|
curapp.id = str;
|
||||||
|
else if (curel == "name")
|
||||||
|
curapp.name = str;
|
||||||
|
else if (curel == "icon")
|
||||||
|
curapp.icon = str;
|
||||||
|
else if (curel == "description")
|
||||||
|
curapp.description = str;
|
||||||
|
else if (curel == "summary")
|
||||||
|
curapp.summary = str;
|
||||||
|
else if (curel == "license")
|
||||||
|
curapp.license = str;
|
||||||
|
else if (curel == "source")
|
||||||
|
curapp.sourceURL = str;
|
||||||
|
else if (curel == "web")
|
||||||
|
curapp.webURL = str;
|
||||||
|
else if (curel == "tracker")
|
||||||
|
curapp.trackerURL = str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String qName)
|
||||||
|
throws SAXException {
|
||||||
|
|
||||||
|
super.endElement(uri, localName, qName);
|
||||||
|
|
||||||
|
if (localName == "application" && curapp != null) {
|
||||||
|
Log.d("FDroid", "Repo: Updating application " + curapp.id);
|
||||||
|
db.updateApplication(curapp);
|
||||||
|
getIcon(curapp);
|
||||||
|
curapp = null;
|
||||||
|
} else if (localName == "package" && curapk != null && curapp != null) {
|
||||||
|
Log.d("FDroid", "Repo: Package added (" + curapk.version + ")");
|
||||||
|
curapp.apks.add(curapk);
|
||||||
|
curapk = null;
|
||||||
|
} else {
|
||||||
|
curel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startElement(String uri, String localName, String qName,
|
||||||
|
Attributes attributes) throws SAXException {
|
||||||
|
|
||||||
|
super.startElement(uri, localName, qName, attributes);
|
||||||
|
if (localName == "application" && curapp == null) {
|
||||||
|
Log.d("FDroid", "Repo: Found application at " + mserver);
|
||||||
|
curapp = new DB.App();
|
||||||
|
} else if (localName == "package" && curapp != null && curapk == null) {
|
||||||
|
Log.d("FDroid", "Repo: Found package for " + curapp.id);
|
||||||
|
curapk = new DB.Apk();
|
||||||
|
curapk.id = curapp.id;
|
||||||
|
curapk.server = mserver;
|
||||||
|
} else {
|
||||||
|
curel = localName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getIcon(DB.App app) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
String destpath = mctx.getString(R.string.icons_path) + app.icon;
|
||||||
|
BufferedInputStream getit = new BufferedInputStream(new URL(mserver
|
||||||
|
+ "/icons/" + app.icon).openStream());
|
||||||
|
File f = new File(destpath);
|
||||||
|
if (f.exists())
|
||||||
|
f.delete();
|
||||||
|
|
||||||
|
FileOutputStream saveit = new FileOutputStream(destpath);
|
||||||
|
BufferedOutputStream bout = new BufferedOutputStream(saveit, 1024);
|
||||||
|
byte data[] = new byte[1024];
|
||||||
|
|
||||||
|
int readed = getit.read(data, 0, 1024);
|
||||||
|
while (readed != -1) {
|
||||||
|
bout.write(data, 0, readed);
|
||||||
|
readed = getit.read(data, 0, 1024);
|
||||||
|
}
|
||||||
|
bout.close();
|
||||||
|
getit.close();
|
||||||
|
saveit.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user