diff options
Diffstat (limited to 'app')
61 files changed, 4006 insertions, 0 deletions
diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml new file mode 100644 index 0000000..ba324d2 --- /dev/null +++ b/app/AndroidManifest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="fil.libre.repwifiapp" + android:versionCode="0" + android:versionName="0.0.9" > + + <uses-sdk + android:minSdkVersion="17" + android:targetSdkVersion="17" /> + + <uses-permission android:name="android.permission.ACCESS_SUPERUSER" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + + <application + android:icon="@drawable/ic_launcher_bis" + android:label="@string/app_name" + android:theme="@style/AppTheme" + android:allowBackup="true" > + <activity + android:name="fil.libre.repwifiapp.activities.MainActivity" + android:screenOrientation="portrait" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.SelectNetworkActivity" + android:label="@string/title_activity_select_network" > + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.InputPasswordActivity" android:label="@string/title_activity_input_password" + android:windowSoftInputMode="adjustResize|stateVisible" > + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.ConnectActivity" + android:label="@string/title_activity_connect" > + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.ShowStatusActivity" + android:label="@string/title_activity_show_status" > + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.LongTaskActivity" > + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.NetworkDetailsActivity" > + </activity> + <activity + android:name="fil.libre.repwifiapp.activities.CreditsActivity"> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/app/assets/repwifi-logo-0.png b/app/assets/repwifi-logo-0.png Binary files differnew file mode 100644 index 0000000..c9613ae --- /dev/null +++ b/app/assets/repwifi-logo-0.png diff --git a/app/gen/fil/libre/repwifiapp/BuildConfig.java b/app/gen/fil/libre/repwifiapp/BuildConfig.java new file mode 100644 index 0000000..c886576 --- /dev/null +++ b/app/gen/fil/libre/repwifiapp/BuildConfig.java @@ -0,0 +1,6 @@ +/** Automatically generated file. DO NOT MODIFY */ +package fil.libre.repwifiapp; + +public final class BuildConfig { + public final static boolean DEBUG = true; +}
\ No newline at end of file diff --git a/app/gen/fil/libre/repwifiapp/R.java b/app/gen/fil/libre/repwifiapp/R.java new file mode 100644 index 0000000..e145272 --- /dev/null +++ b/app/gen/fil/libre/repwifiapp/R.java @@ -0,0 +1,128 @@ +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package fil.libre.repwifiapp; + +public final class R { + public static final class attr { + } + public static final class color { + public static final int ThemeDark=0x7f040001; + public static final int ThemeLight=0x7f040000; + public static final int White=0x7f040003; + public static final int black=0x7f040002; + } + public static final class drawable { + public static final int ic_launcher=0x7f020000; + public static final int ic_launcher2=0x7f020001; + public static final int ic_launcher_bis=0x7f020002; + public static final int ic_launcher_tris=0x7f020003; + } + public static final class id { + public static final int btn_back=0x7f080017; + public static final int btn_delete=0x7f08000f; + public static final int btn_disconnect=0x7f080016; + public static final int btn_manage_nets=0x7f08000b; + public static final int btn_rescan=0x7f080012; + public static final int btn_scan=0x7f08000a; + public static final int chk_show_pass=0x7f080004; + public static final int chk_show_pass_details=0x7f08000e; + public static final int img_logo=0x7f08000c; + public static final int layout=0x7f080006; + public static final int layout_selnets=0x7f080010; + public static final int login_form=0x7f080001; + public static final int menu_credits=0x7f080018; + public static final int progbar=0x7f080007; + public static final int scrollview=0x7f080013; + public static final int sign_in_button=0x7f080005; + public static final int table_networks=0x7f080014; + public static final int txt_credits=0x7f080000; + public static final int txt_insert_pass=0x7f080002; + public static final int txt_main=0x7f080009; + public static final int txt_msg=0x7f080008; + public static final int txt_net_details=0x7f08000d; + public static final int txt_password=0x7f080003; + public static final int txt_selnets=0x7f080011; + public static final int txt_status=0x7f080015; + } + public static final class layout { + public static final int activity_credits=0x7f030000; + public static final int activity_input_password=0x7f030001; + public static final int activity_long_task=0x7f030002; + public static final int activity_main=0x7f030003; + public static final int activity_network_details=0x7f030004; + public static final int activity_select_network=0x7f030005; + public static final int activity_show_status=0x7f030006; + } + public static final class menu { + public static final int activity_main=0x7f070000; + } + public static final class string { + public static final int action_sign_in_register=0x7f050017; + public static final int action_sign_in_short=0x7f050018; + public static final int app_name=0x7f050000; + public static final int back_main=0x7f05000b; + public static final int button_text_next=0x7f050005; + public static final int credit_text=0x7f050013; + public static final int delete=0x7f050010; + public static final int disconnect=0x7f05000c; + public static final int error_field_required=0x7f05001e; + public static final int error_incorrect_password=0x7f05001d; + public static final int error_invalid_email=0x7f05001b; + public static final int error_invalid_password=0x7f05001c; + public static final int force_disconnect=0x7f05000d; + public static final int hello_world=0x7f050009; + public static final int insert_nets_password=0x7f050006; + public static final int login_progress_signing_in=0x7f05001a; + public static final int manage_networks=0x7f050011; + public static final int menu_credits=0x7f050001; + public static final int menu_forgot_password=0x7f050019; + /** Strings related to login + */ + public static final int prompt_email=0x7f050015; + public static final int prompt_password=0x7f050016; + public static final int rescan=0x7f050004; + public static final int scan_networks=0x7f050002; + public static final int show_password=0x7f050007; + public static final int title_activity_connect=0x7f050008; + public static final int title_activity_credits=0x7f050012; + public static final int title_activity_input_password=0x7f050014; + public static final int title_activity_long_task=0x7f05000e; + public static final int title_activity_manage_networks=0x7f05000f; + public static final int title_activity_select_network=0x7f050003; + public static final int title_activity_show_status=0x7f05000a; + } + public static final class style { + /** + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + + + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + + + Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices. + + API 11 theme customizations can go here. + + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + + API 14 theme customizations can go here. + */ + public static final int AppBaseTheme=0x7f060000; + /** Application theme. + All customizations that are NOT specific to a particular API-level can go here. + */ + public static final int AppTheme=0x7f060001; + public static final int LoginFormContainer=0x7f060002; + } +} diff --git a/app/ic_launcher-web.png b/app/ic_launcher-web.png Binary files differnew file mode 100644 index 0000000..df56b4e --- /dev/null +++ b/app/ic_launcher-web.png diff --git a/app/ic_launcher2-web.png b/app/ic_launcher2-web.png Binary files differnew file mode 100644 index 0000000..e0a9dc8 --- /dev/null +++ b/app/ic_launcher2-web.png diff --git a/app/ic_launcher_bis-web.png b/app/ic_launcher_bis-web.png Binary files differnew file mode 100644 index 0000000..8a9e987 --- /dev/null +++ b/app/ic_launcher_bis-web.png diff --git a/app/ic_launcher_tris-web.png b/app/ic_launcher_tris-web.png Binary files differnew file mode 100644 index 0000000..a5e4d32 --- /dev/null +++ b/app/ic_launcher_tris-web.png diff --git a/app/libs/android-support-v4.jar b/app/libs/android-support-v4.jar Binary files differnew file mode 100644 index 0000000..229e4eb --- /dev/null +++ b/app/libs/android-support-v4.jar diff --git a/app/previews.xml b/app/previews.xml new file mode 100644 index 0000000..daf3adb --- /dev/null +++ b/app/previews.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="utf-8"?> +<previews> + + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + <preview + name="Config1" + activity="com.example.repwifiapp.CreditsActivity" + config="layout-sw320dp-w320dp-h533dp-normal-long-port-notnight-hdpi-finger-keyssoft-nokeys-navexposed-trackball-800x480-v17" + device="3.7in WVGA (Nexus One)" + devicestate="Portrait" + target="Android 4.2.2" + theme="@style/AppTheme" > + </preview> + +</previews>
\ No newline at end of file diff --git a/app/proguard-project.txt b/app/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/app/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/project.properties b/app/project.properties new file mode 100644 index 0000000..a3ee5ab --- /dev/null +++ b/app/project.properties @@ -0,0 +1,14 @@ +# 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 edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/app/res/drawable-hdpi/ic_launcher.png b/app/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..50093ae --- /dev/null +++ b/app/res/drawable-hdpi/ic_launcher.png diff --git a/app/res/drawable-hdpi/ic_launcher2.png b/app/res/drawable-hdpi/ic_launcher2.png Binary files differnew file mode 100644 index 0000000..78d26b2 --- /dev/null +++ b/app/res/drawable-hdpi/ic_launcher2.png diff --git a/app/res/drawable-hdpi/ic_launcher_bis.png b/app/res/drawable-hdpi/ic_launcher_bis.png Binary files differnew file mode 100644 index 0000000..1ddfad3 --- /dev/null +++ b/app/res/drawable-hdpi/ic_launcher_bis.png diff --git a/app/res/drawable-hdpi/ic_launcher_tris.png b/app/res/drawable-hdpi/ic_launcher_tris.png Binary files differnew file mode 100644 index 0000000..ddaa253 --- /dev/null +++ b/app/res/drawable-hdpi/ic_launcher_tris.png diff --git a/app/res/drawable-mdpi/ic_launcher.png b/app/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..e2fe865 --- /dev/null +++ b/app/res/drawable-mdpi/ic_launcher.png diff --git a/app/res/drawable-mdpi/ic_launcher2.png b/app/res/drawable-mdpi/ic_launcher2.png Binary files differnew file mode 100644 index 0000000..4a61a75 --- /dev/null +++ b/app/res/drawable-mdpi/ic_launcher2.png diff --git a/app/res/drawable-mdpi/ic_launcher_bis.png b/app/res/drawable-mdpi/ic_launcher_bis.png Binary files differnew file mode 100644 index 0000000..c205105 --- /dev/null +++ b/app/res/drawable-mdpi/ic_launcher_bis.png diff --git a/app/res/drawable-mdpi/ic_launcher_tris.png b/app/res/drawable-mdpi/ic_launcher_tris.png Binary files differnew file mode 100644 index 0000000..935010c --- /dev/null +++ b/app/res/drawable-mdpi/ic_launcher_tris.png diff --git a/app/res/drawable-xhdpi/ic_launcher.png b/app/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71ed985 --- /dev/null +++ b/app/res/drawable-xhdpi/ic_launcher.png diff --git a/app/res/drawable-xhdpi/ic_launcher2.png b/app/res/drawable-xhdpi/ic_launcher2.png Binary files differnew file mode 100644 index 0000000..01fc7ef --- /dev/null +++ b/app/res/drawable-xhdpi/ic_launcher2.png diff --git a/app/res/drawable-xhdpi/ic_launcher_bis.png b/app/res/drawable-xhdpi/ic_launcher_bis.png Binary files differnew file mode 100644 index 0000000..4a0f08f --- /dev/null +++ b/app/res/drawable-xhdpi/ic_launcher_bis.png diff --git a/app/res/drawable-xhdpi/ic_launcher_tris.png b/app/res/drawable-xhdpi/ic_launcher_tris.png Binary files differnew file mode 100644 index 0000000..5992ef4 --- /dev/null +++ b/app/res/drawable-xhdpi/ic_launcher_tris.png diff --git a/app/res/drawable-xxhdpi/ic_launcher.png b/app/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..5545989 --- /dev/null +++ b/app/res/drawable-xxhdpi/ic_launcher.png diff --git a/app/res/drawable-xxhdpi/ic_launcher2.png b/app/res/drawable-xxhdpi/ic_launcher2.png Binary files differnew file mode 100644 index 0000000..aaf68c6 --- /dev/null +++ b/app/res/drawable-xxhdpi/ic_launcher2.png diff --git a/app/res/drawable-xxhdpi/ic_launcher_bis.png b/app/res/drawable-xxhdpi/ic_launcher_bis.png Binary files differnew file mode 100644 index 0000000..ea36964 --- /dev/null +++ b/app/res/drawable-xxhdpi/ic_launcher_bis.png diff --git a/app/res/drawable-xxhdpi/ic_launcher_tris.png b/app/res/drawable-xxhdpi/ic_launcher_tris.png Binary files differnew file mode 100644 index 0000000..0bf88f3 --- /dev/null +++ b/app/res/drawable-xxhdpi/ic_launcher_tris.png diff --git a/app/res/layout/activity_credits.xml b/app/res/layout/activity_credits.xml new file mode 100644 index 0000000..871536c --- /dev/null +++ b/app/res/layout/activity_credits.xml @@ -0,0 +1,17 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/ThemeDark"> + + <TextView + android:id="@+id/txt_credits" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:textColor="@color/ThemeLight" + android:textColorLink="@color/ThemeLight" + android:textSize="7pt" + android:scrollbars = "vertical" /> + + +</LinearLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_input_password.xml b/app/res/layout/activity_input_password.xml new file mode 100644 index 0000000..a024879 --- /dev/null +++ b/app/res/layout/activity_input_password.xml @@ -0,0 +1,65 @@ +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".InputPasswordActivity" > + + + + <ScrollView + android:id="@+id/login_form" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background = "@color/black" > + + <LinearLayout + style="@style/LoginFormContainer" + android:orientation="vertical" + android:background="@color/black" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <TextView + android:id="@+id/txt_insert_pass" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textCursorDrawable="@null" + android:textColor="@color/ThemeLight" + android:textSize="10pt" + android:layout_marginBottom="10dp" + android:background="@color/black" + android:maxLines = "10" + android:text="@string/insert_nets_password" /> + + <EditText + android:id="@+id/txt_password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textPassword" + android:textColor="@color/ThemeLight" + android:textSize="10pt" + android:maxLines="1" + android:singleLine="true" /> + + <CheckBox + android:id="@+id/chk_show_pass" + android:textColor="@color/ThemeLight" + android:text="@string/show_password" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button + android:id="@+id/sign_in_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@color/ThemeDark" + android:textColor="@color/ThemeLight" + android:layout_gravity="right" + android:layout_marginTop="16dp" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:text="@string/button_text_next" + android:onClick="onBtnNextClick" /> + + </LinearLayout> + </ScrollView> + +</merge>
\ No newline at end of file diff --git a/app/res/layout/activity_long_task.xml b/app/res/layout/activity_long_task.xml new file mode 100644 index 0000000..769144e --- /dev/null +++ b/app/res/layout/activity_long_task.xml @@ -0,0 +1,25 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/black" > + + <ProgressBar + android:id="@+id/progbar" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" /> + + <TextView + android:id="@+id/txt_msg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:layout_below="@id/progbar" + android:padding="10dp" + android:textColor="@color/ThemeLight" /> + + +</RelativeLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_main.xml b/app/res/layout/activity_main.xml new file mode 100644 index 0000000..3818751 --- /dev/null +++ b/app/res/layout/activity_main.xml @@ -0,0 +1,57 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:padding="15dp" + android:background="@color/black" > + + <TextView + android:id="@+id/txt_main" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textColor="@color/ThemeLight" + android:background="@color/black" + android:maxLines = "10" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" + android:text="" /> + + <Button + android:id="@+id/btn_scan" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:layout_centerHorizontal="true" + android:textColor="@color/ThemeLight" + android:background="@color/ThemeDark" + android:text="@string/scan_networks" + android:onClick="btnScanClick" + android:layout_marginTop="15dp" + android:layout_marginBottom="20dp" /> + + <Button + android:id="@+id/btn_manage_nets" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/btn_scan" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:layout_centerHorizontal="true" + android:textColor="@color/ThemeLight" + android:background="@color/ThemeDark" + android:text="@string/manage_networks" + android:onClick="btnManageClick" + android:layout_marginBottom="20dp" /> + + <ImageView + android:id="@+id/img_logo" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_centerHorizontal="true" + android:layout_below="@id/btn_manage_nets" + android:layout_margin="20dp" > + + </ImageView> + + </RelativeLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_network_details.xml b/app/res/layout/activity_network_details.xml new file mode 100644 index 0000000..a36c957 --- /dev/null +++ b/app/res/layout/activity_network_details.xml @@ -0,0 +1,38 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="15dp" + android:background="@color/black" + tools:context=".NetworkDetailsActivity" > + + <TextView + android:id="@+id/txt_net_details" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="10dp" + android:textColor="@color/ThemeLight" + android:textSize="7pt" + android:background="@color/black" /> + + <CheckBox + android:id="@+id/chk_show_pass_details" + android:layout_below="@id/txt_net_details" + android:textColor="@color/ThemeLight" + android:text="@string/show_password" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button + android:id="@+id/btn_delete" + android:layout_below="@id/chk_show_pass_details" + android:layout_marginTop="15dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textColor="@color/ThemeLight" + android:background="@color/ThemeDark" + android:text="@string/delete" + android:onClick="btnDeleteClick" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_select_network.xml b/app/res/layout/activity_select_network.xml new file mode 100644 index 0000000..2dd7958 --- /dev/null +++ b/app/res/layout/activity_select_network.xml @@ -0,0 +1,58 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/layout_selnets" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="15dp" + tools:context=".SelectNetworkActivity" + android:background="@color/black" > + + <TextView + android:id="@+id/txt_selnets" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textColor="@color/ThemeLight" + android:textSize="10pt" + android:maxLines = "5" + android:scrollbars = "vertical" + android:layout_marginBottom="10dp" /> + + <Button + android:id="@+id/btn_rescan" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/txt_selnets" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:layout_centerHorizontal="true" + android:textColor="@color/ThemeLight" + android:background="@color/ThemeDark" + android:text="@string/rescan" + android:onClick="btnScanClick" /> + + <ScrollView + android:id="@+id/scrollview" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layout_below="@id/btn_rescan" + android:layout_centerHorizontal="true" + android:scrollbars="vertical" + android:fillViewport="true" + android:textColor="@color/ThemeLight" + android:background="@color/White" > + + <TableLayout + android:id="@+id/table_networks" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_gravity="fill_horizontal" + android:isScrollContainer="true" + android:textColor="@color/ThemeLight" + android:background="@color/black" > + </TableLayout> + + + </ScrollView> + +</RelativeLayout>
\ No newline at end of file diff --git a/app/res/layout/activity_show_status.xml b/app/res/layout/activity_show_status.xml new file mode 100644 index 0000000..9f7e584 --- /dev/null +++ b/app/res/layout/activity_show_status.xml @@ -0,0 +1,43 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="15dp" + android:background="@color/black" + tools:context=".ShowStatusActivity" > + + <TextView + android:id="@+id/txt_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:background="@color/black" + android:textColor="@color/ThemeLight" /> + + <Button + android:id="@+id/btn_disconnect" + android:background="@color/ThemeDark" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_centerHorizontal="true" + android:layout_below="@id/txt_status" + android:layout_marginTop="10dp" + android:textColor="@color/ThemeLight" + android:text="@string/disconnect" + android:onClick="onBtnDisconnectClick"/> + + <Button + android:id="@+id/btn_back" + android:background="@color/ThemeDark" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_centerHorizontal="true" + android:layout_below="@id/txt_status" + android:layout_marginTop="10dp" + android:textColor="@color/ThemeLight" + android:text="@string/back_main" + android:onClick="onBtnMainClick"/> + +</RelativeLayout>
\ No newline at end of file diff --git a/app/res/menu/activity_main.xml b/app/res/menu/activity_main.xml new file mode 100644 index 0000000..cd7c0cb --- /dev/null +++ b/app/res/menu/activity_main.xml @@ -0,0 +1,9 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + + <item + android:id="@+id/menu_credits" + android:orderInCategory="100" + android:showAsAction="never" + android:title="@string/menu_credits" /> + +</menu>
\ No newline at end of file diff --git a/app/res/values-large/styles.xml b/app/res/values-large/styles.xml new file mode 100644 index 0000000..acd524d --- /dev/null +++ b/app/res/values-large/styles.xml @@ -0,0 +1,10 @@ +<resources> + + <style name="LoginFormContainer"> + <item name="android:layout_width">400dp</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center</item> + <item name="android:padding">16dp</item> + </style> + +</resources>
\ No newline at end of file diff --git a/app/res/values-v11/styles.xml b/app/res/values-v11/styles.xml new file mode 100644 index 0000000..541752f --- /dev/null +++ b/app/res/values-v11/styles.xml @@ -0,0 +1,11 @@ +<resources> + + <!-- + Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light"> + <!-- API 11 theme customizations can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/app/res/values-v14/styles.xml b/app/res/values-v14/styles.xml new file mode 100644 index 0000000..f20e015 --- /dev/null +++ b/app/res/values-v14/styles.xml @@ -0,0 +1,12 @@ +<resources> + + <!-- + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar"> + <!-- API 14 theme customizations can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/app/res/values/colors.xml b/app/res/values/colors.xml new file mode 100644 index 0000000..22767c8 --- /dev/null +++ b/app/res/values/colors.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="ThemeLight">#FFA500</color> + <color name="ThemeDark">#5F021F</color> + <color name="black">#000000</color> + <color name="White">#FFFFFF</color> +</resources>
\ No newline at end of file diff --git a/app/res/values/strings.xml b/app/res/values/strings.xml new file mode 100644 index 0000000..00168b5 --- /dev/null +++ b/app/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">RepWifi App</string> + <string name="menu_credits">Info and Credits</string> + <string name="scan_networks">Scan for Networks</string> + <string name="title_activity_select_network">Select Network</string> + <string name="rescan">Repeat Scan</string> + <string name="button_text_next">Next ></string> + <string name="insert_nets_password">Insert a Password to connect to:</string> + <string name="show_password">Show password</string> + <string name="title_activity_connect">ConnectActivity</string> + <string name="hello_world">Hello world!</string> + <string name="title_activity_show_status">Connection status</string> + <string name="back_main">Back to main</string> + <string name="disconnect">Disconnect</string> + <string name="force_disconnect">Force Disconnection</string> + <string name="title_activity_long_task">LongTaskActivity</string> + <string name="title_activity_manage_networks">ManageNetworksActivity</string> + <string name="delete">Delete network info</string> + <string name="manage_networks">Manage Networks</string> + <string name="title_activity_credits">Credits</string> + <string name="credit_text">Copyright 2017 Filippo \"Fil\" Bergamo<br/><br/>This app is Free Software.<br/>It\'s licensed under the terms of <a href="https://www.gnu.org/licenses/gpl.txt">GPL v3</a><br/><br/>This app is developed as a contribution to <br/><a href="http://www.replicant.us">The Replicant Project</a><br/><br/><b><u><big>Thank GNUs:</big></u></b><br/><br/><b>Tibi \"tct\" Turbureanu</b><br/>for having done the initial job of porting libre WiFi to Replicant 4.2.<br/><br/><b>Wolfgang Wiedmeyer</b><br/>for porting libre WiFi to Replicant 6.0 and for helping with the scripts.<br/><br/>To report on bugs, request features, or any help request, please refer to:<br/><a href="http://redmine.replicant.us/projects/replicant/boards">Replicant\'s Forum</a><br/></string> + +</resources>
\ No newline at end of file diff --git a/app/res/values/strings_activity_input_password.xml b/app/res/values/strings_activity_input_password.xml new file mode 100644 index 0000000..4770a51 --- /dev/null +++ b/app/res/values/strings_activity_input_password.xml @@ -0,0 +1,17 @@ +<resources> + + <string name="title_activity_input_password">Insert passwrod for network</string> + + <!-- Strings related to login --> + <string name="prompt_email">Email</string> + <string name="prompt_password">Password</string> + <string name="action_sign_in_register"><b>Sign in</b> or register</string> + <string name="action_sign_in_short">Sign in</string> + <string name="menu_forgot_password">Recover lost password</string> + <string name="login_progress_signing_in">Signing in…</string> + <string name="error_invalid_email">This email address is invalid</string> + <string name="error_invalid_password">This password is too short</string> + <string name="error_incorrect_password">This password is incorrect</string> + <string name="error_field_required">This field is required</string> + +</resources>
\ No newline at end of file diff --git a/app/res/values/styles.xml b/app/res/values/styles.xml new file mode 100644 index 0000000..c414ecc --- /dev/null +++ b/app/res/values/styles.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/app/src/fil/libre/repwifiapp/Commons.java b/app/src/fil/libre/repwifiapp/Commons.java new file mode 100644 index 0000000..dcf8893 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/Commons.java @@ -0,0 +1,156 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp; + +import java.io.File; + +import fil.libre.repwifiapp.helpers.Engine4p2; +import fil.libre.repwifiapp.helpers.Engine6p0; +import fil.libre.repwifiapp.helpers.IEngine; +import fil.libre.repwifiapp.helpers.NetworkManager; + + + +public abstract class Commons { + + + //------------- Enviromnet Constants ---------------------------------------- + public static final String v4p2 = "4.2"; + public static final String v6p0 = "6.0"; + public static final String SCAN_FILE_HDR = "bssid / frequency / signal level / flags / ssid"; + public static final String INTERFACE_NAME="wlan0"; + public static final String WORKDIR = "/data/misc/wifi"; + public static final String PID_FILE = WORKDIR + "/pidfile"; + public static final String SOCKET_DIR = WORKDIR + "/sockets/"; + public static final String SOFTAP_FILE = WORKDIR + "/softap.conf"; + public static final String P2P_CONF = WORKDIR + "/p2p_supplicant.conf"; + public static final String WPA_CONF = WORKDIR + "/wpa_supplicant.conf"; + public static final String ENTROPY_FILE = WORKDIR + "/entropy.bin"; + public static final String OVERLAY_FILE = "/system/etc/wifi/wpa_supplicant_overlay.conf"; + //------------------------------------------------------------------------------ + + //------------- Shared Engines ----------------------- + public static IEngine connectionEngine = null; + public static NetworkManager storage = null; + //---------------------------------------------------- + + + //------------- Shared Resources --------------------- + public static int colorThemeDark; + public static int colorThemeLight; + public static int colorBlack; + //---------------------------------------------------- + + //------------- Activity Interaction ----------------- + public static final String EXTRA_APINFO = "ExAPInfo"; + public static final String EXTRA_APINFO_ARR = "ExAPInfoArr"; + public static final String EXTRA_CONSTATUS = "ExConSts"; + public static final String EXTRA_BOOLEAN = "ExBool"; + public static final String EXTRA_REQCODE = "ExReqCode"; + public static final String EXTRA_RESCAN = "ExRescan"; + public static final String EXTRA_DELETE = "ExDelete"; + + public class RequestCode{ + public static final int NONE = 0; + public static final int SELECT_CONN = 1; + public static final int PASS_INPUT = 2; + public static final int STATUS_SHOW = 3; + public static final int STATUS_GET = 4; + public static final int CONNECT = 5; + public static final int NETWORKS_GET = 6; + public static final int SELECT_DETAILS = 7; + public static final int DETAILS_SHOW = 8; + public static final int NETWORK_DELETE = 9; + } + //---------------------------------------------------- + + + //----------------- Application Files -------------------- + private static String APP_DATA_FOLDER; + public static void setAppDataFolder(String path){ + File f = new File(path); + if (f.exists()){ + APP_DATA_FOLDER = path; + } + } + public static String getNetworkStorageFile(){ + if (APP_DATA_FOLDER == null){ + return null; + }else{ + return APP_DATA_FOLDER + "/repwifi_storage.conf"; + } + } + public static String getScriptScan(){ + return APP_DATA_FOLDER + "/scan.sh"; + } + public static String getScriptScanRes(){ + return APP_DATA_FOLDER + "/get_scan_results.sh"; + } + public static String getScriptDhcpcd(){ + return APP_DATA_FOLDER + "/run_dhcpcd.sh"; + } + public static String getScanFile(){ + return APP_DATA_FOLDER + "/scanres.txt"; + } + public static String getStatusFile(){ + return APP_DATA_FOLDER + "/tmpStatus"; + } + public static String getGwFile(){ + return APP_DATA_FOLDER + "/gw.txt"; + } + public static String getTempOutFile(){ + return APP_DATA_FOLDER + "/tmpout.txt"; + } + //-------------------------------------------------------- + + + //----------- Initialization methods --------------------------- + public static void initObjects()throws Exception{ + + initEngine(); + initNetworkStorage(); + } + + private static void initEngine() throws Exception{ + + String vers = android.os.Build.VERSION.RELEASE; + + if (vers.startsWith(Commons.v4p2)){ + Commons.connectionEngine = new Engine4p2(); + } + else if(vers.startsWith(Commons.v6p0)){ + Commons.connectionEngine = new Engine6p0(); + } + else{ + throw new Exception("System version not recognized!"); + } + + } + + private static void initNetworkStorage() throws Exception{ + + Commons.storage = new NetworkManager(getNetworkStorageFile()); + + } + //-------------------------------------------------------------- + + +} diff --git a/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java b/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java new file mode 100644 index 0000000..4a0cb4c --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java @@ -0,0 +1,56 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + +import fil.libre.repwifiapp.R; + +import android.os.Bundle; +import android.app.Activity; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.text.method.ScrollingMovementMethod; +import android.view.Menu; +import android.widget.TextView; + +public class CreditsActivity extends Activity { + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_credits); + + String content = getResources().getString(R.string.credit_text); + TextView tv = (TextView)findViewById(R.id.txt_credits); + tv.setMovementMethod(new ScrollingMovementMethod()); + tv.setText(Html.fromHtml(content)); + tv.setMovementMethod(LinkMovementMethod.getInstance()); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + //getMenuInflater().inflate(R.menu.activity_credits, menu); + return true; + } + +} diff --git a/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java b/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java new file mode 100644 index 0000000..e46d7d9 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java @@ -0,0 +1,112 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + + +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.AccessPointInfo; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.text.InputType; +import android.view.Menu; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.EditText; +import android.widget.TextView; + + +public class InputPasswordActivity extends Activity implements OnCheckedChangeListener{ + + AccessPointInfo apinfo = null; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.activity_input_password); + + CheckBox c = (CheckBox)findViewById(R.id.chk_show_pass); + c.setOnCheckedChangeListener(this); + + setTitle("Input password"); + + TextView v = (TextView)findViewById(R.id.txt_insert_pass); + + //get the network to set password to: + this.apinfo = (AccessPointInfo)getIntent().getSerializableExtra(Commons.EXTRA_APINFO); + v.append(" " + apinfo.getSSID()); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + //super.onCreateOptionsMenu(menu); + //getMenuInflater().inflate(R.menu.activity_input_password, menu); + return true; + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + + if(buttonView == findViewById(R.id.chk_show_pass)){ + chkShowPassChanged(); + } + } + + public void onBtnNextClick(View v){ + + EditText txpass = (EditText)findViewById(R.id.txt_password); + String pass = txpass.getText().toString(); + if (pass.length()>0){ + + this.apinfo.setPassword(pass); + + Intent intent = new Intent(); + intent.putExtra(Commons.EXTRA_APINFO, this.apinfo); + setResult(RESULT_OK, intent); + finish(); + + } + + + } + + public void chkShowPassChanged(){ + + CheckBox c = (CheckBox)findViewById(R.id.chk_show_pass); + EditText txtPass = (EditText)findViewById(R.id.txt_password); + + if (c.isChecked()){ + txtPass.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + txtPass.setSelection(txtPass.getText().length()); + }else{ + txtPass.setInputType(129); + txtPass.setSelection(txtPass.getText().length()); + } + + } + +} diff --git a/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java b/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java new file mode 100644 index 0000000..a9f010a --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java @@ -0,0 +1,174 @@ +package fil.libre.repwifiapp.activities; + + +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.AccessPointInfo; +import fil.libre.repwifiapp.helpers.ConnectionStatus; +import fil.libre.repwifiapp.helpers.Utils; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.Menu; +import android.widget.TextView; +import android.app.Activity; +import android.content.Intent; + +public class LongTaskActivity extends Activity { + + + private class Task extends AsyncTask<Object, Object, Object>{ + + private int REQ_CODE; + + public Task(int reqCode, Object input){ + this.REQ_CODE = reqCode; + } + + @Override + protected Object doInBackground(Object... params) { + + Object ret = null; + + switch (this.REQ_CODE){ + + case Commons.RequestCode.CONNECT: + + ret = Commons.connectionEngine.connect((AccessPointInfo)params[0]); + break; + + case Commons.RequestCode.NETWORKS_GET: + + ret = Commons.connectionEngine.getAvailableNetworks(); + break; + + case Commons.RequestCode.STATUS_GET: + + ret = Commons.connectionEngine.getConnectionStatus(); + break; + + default: + + break; + + } + + return ret; + + } + + protected void onPostExecute(Object result) { + taskCompleted(result, this.REQ_CODE); + } + + } + + private AccessPointInfo currentNetwork = null; + + @Override + public void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_long_task); + + startTask(); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu){ + return true; + } + + private void startTask() { + + //retrieve the request code: + Intent intent = getIntent(); + if (! intent.hasExtra(Commons.EXTRA_REQCODE)){ + this.setResult(RESULT_CANCELED); + finish(); + } + + Object input = null; + int reqCode = intent.getExtras().getInt(Commons.EXTRA_REQCODE); + + switch (reqCode) { + + case Commons.RequestCode.CONNECT: + setTitle("Connecting..."); + setMessage("Connecting..."); + //Extract AccessPointInfo + input = intent.getExtras().getSerializable(Commons.EXTRA_APINFO); + currentNetwork = (AccessPointInfo)input; + break; + + case Commons.RequestCode.NETWORKS_GET: + setTitle("Scanning..."); + setMessage("Scanning for Networks..."); + + case Commons.RequestCode.STATUS_GET: + setTitle("Checking status..."); + setMessage("Checking status..."); + + default: + setTitle("Please wait..."); + setMessage("Please wait..."); + break; + } + + Task task = new Task(reqCode, input); + task.execute(input); + + } + + private void taskCompleted(Object result, int reqCode){ + + Utils.logDebug("Finished long task reqCode: "+ reqCode,1); + + //Return to caller: + Intent intent = this.getIntent(); + + + switch (reqCode){ + + case Commons.RequestCode.CONNECT: + + intent.putExtra(Commons.EXTRA_BOOLEAN, (Boolean)result); + intent.putExtra(Commons.EXTRA_APINFO, this.currentNetwork); + break; + + case Commons.RequestCode.NETWORKS_GET: + + intent.putExtra(Commons.EXTRA_APINFO_ARR, (AccessPointInfo[])result); + break; + + case Commons.RequestCode.STATUS_GET: + + intent.putExtra(Commons.EXTRA_CONSTATUS, (ConnectionStatus)result); + break; + + default: + + Utils.logDebug("Task terminating in null: ",1); + break; + + } + + this.setResult(RESULT_OK, intent); + finish(); + + } + + + private void setMessage(String msg) { + TextView txt = (TextView)findViewById(R.id.txt_msg); + txt.setText(msg); + } + + @Override + public void onBackPressed() { + //suppress + } + + + +} diff --git a/app/src/fil/libre/repwifiapp/activities/MainActivity.java b/app/src/fil/libre/repwifiapp/activities/MainActivity.java new file mode 100644 index 0000000..aa92767 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/MainActivity.java @@ -0,0 +1,378 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + + + + + +import java.io.IOException; + +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Commons.RequestCode; +import fil.libre.repwifiapp.helpers.AccessPointInfo; +import fil.libre.repwifiapp.helpers.ConnectionStatus; +import fil.libre.repwifiapp.helpers.NetworkManager; +import fil.libre.repwifiapp.helpers.RootCommand; +import fil.libre.repwifiapp.helpers.Utils; + +import android.os.Bundle; +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.Toast; + +public class MainActivity extends Activity{ + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + setImage(); + setupSharedResources(); + + RootCommand su = new RootCommand(null); + try { + su.execute(); + } catch (Exception e) { + Utils.logError("Error while trying to get first Super User access. Aborting.",e); + finish(); + } + + try { + Commons.initObjects(); + } catch (Exception e) { + Utils.logError("Error on creating engine. Aborting.",e); + finish(); + } + + + checkConnectionStatus(); + + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.activity_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.menu_credits: + launchCreditsActivity(); + return true; + + default: + return true; + } + } + + @Override + public void onRestart(){ + super.onRestart(); + + ConnectionStatus status = Commons.connectionEngine.getConnectionStatus(); + if (status != null && status.isConnected()){ + launchStatusActivity(status); + } + + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent){ + + Utils.logDebug("Main onActivityResult(): ",1); + + if (intent == null){ + return; + } + + if (resultCode != RESULT_OK){ + return; + } + + AccessPointInfo i = null; + if (intent.hasExtra(Commons.EXTRA_APINFO)){ + i = (AccessPointInfo)intent.getExtras().getSerializable(Commons.EXTRA_APINFO); + } + + switch (requestCode) { + + case RequestCode.PASS_INPUT: + handleResultSetPass(i); + break; + + case RequestCode.SELECT_CONN: + boolean rescan = (boolean)intent.getExtras().getBoolean(Commons.EXTRA_RESCAN); + handleResultSelect(i, rescan); + break; + + case RequestCode.CONNECT: + boolean conres = intent.getExtras().getBoolean(Commons.EXTRA_BOOLEAN); + handleFinishedConnecting(conres, i); + break; + + case RequestCode.STATUS_GET: + ConnectionStatus status = (ConnectionStatus)intent.getExtras().getSerializable(Commons.EXTRA_CONSTATUS); + handleResultGetStatus(status); + break; + + case RequestCode.NETWORKS_GET: + AccessPointInfo[] nets = (AccessPointInfo[])intent.getExtras().getSerializable(Commons.EXTRA_APINFO_ARR); + launchSelectActivity(nets, true); + + case RequestCode.STATUS_SHOW: + //do nothing + break; + + case RequestCode.SELECT_DETAILS: + launchDetailsActivity(i); + break; + + case RequestCode.DETAILS_SHOW: + boolean del = intent.getExtras().getBoolean(Commons.EXTRA_DELETE); + if (del){ deleteNetwork(i); } + break; + + default: + + break; + + } + + + } + + private void setImage(){ + + ImageView img = (ImageView)findViewById(R.id.img_logo); + + try { + Drawable d = Drawable.createFromStream(getAssets().open("repwifi-logo-0.png"),null); + img.setImageDrawable(d); + } catch (IOException e) { + Utils.logError("Error while loading logo image",e); + } + + } + + private void setupSharedResources(){ + Commons.colorThemeDark = getResources().getColor(R.color.ThemeDark); + Commons.colorThemeLight = getResources().getColor(R.color.ThemeLight); + Commons.colorBlack = getResources().getColor(R.color.black); + Commons.setAppDataFolder(getExternalFilesDir(null).getAbsolutePath()); + } + + private void handleResultSelect(AccessPointInfo i, boolean rescan){ + + if (rescan){ + + doScan(); + + }else if (i != null){ + + if (i.needsPassword()){ + + //try to fetch network's password from storage + AccessPointInfo fromStorage = Commons.storage.getSavedNetwork(i); + if (fromStorage == null){ + + launchPasswordActivity(i); + return; + + }else{ + //use fetched network + i = fromStorage; + } + + } + + connectToNetwork(i); + } + + } + + private void handleResultSetPass(AccessPointInfo i){ + connectToNetwork(i); + } + + private void handleResultGetStatus(ConnectionStatus status){ + if (status != null && status.isConnected()){ + launchStatusActivity(status); + } + } + + private void handleFinishedConnecting(boolean connectionResult, AccessPointInfo info){ + + if(connectionResult && info.needsPassword()){ + + //Save network + if (Commons.storage.save(info)){ + Toast toast2 = Toast.makeText(getApplicationContext(), "Network Saved!",Toast.LENGTH_LONG); + toast2.show(); + + }else { + Toast toast2 = Toast.makeText(getApplicationContext(), "FAILED to save network!",Toast.LENGTH_LONG); + toast2.show(); + } + + checkConnectionStatus(); + + }else{ + //alert that connection failed + Toast toast = Toast.makeText(getApplicationContext(), "FAILED to connect!", Toast.LENGTH_LONG); + toast.show(); + } + } + + private void launchPasswordActivity(AccessPointInfo info){ + + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.setClass(getApplicationContext(), InputPasswordActivity.class); + intent.putExtra(Commons.EXTRA_APINFO, info); + + startActivityForResult(intent, RequestCode.PASS_INPUT); + + } + + private void launchStatusActivity(ConnectionStatus status){ + + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(Commons.EXTRA_CONSTATUS, status); + intent.setClass(getApplicationContext(), ShowStatusActivity.class); + startActivityForResult(intent, RequestCode.STATUS_SHOW); + + } + + private void launchSelectActivity(AccessPointInfo[] nets,boolean forConnection){ + + Intent intent = new Intent(this, SelectNetworkActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(Commons.EXTRA_APINFO_ARR, nets); + + if (forConnection){ + intent.putExtra(Commons.EXTRA_REQCODE, RequestCode.SELECT_CONN); + startActivityForResult(intent, RequestCode.SELECT_CONN); + } + else{ + intent.putExtra(Commons.EXTRA_REQCODE, RequestCode.SELECT_DETAILS); + startActivityForResult(intent, RequestCode.SELECT_DETAILS); + } + + } + + private void launchDetailsActivity(AccessPointInfo info){ + + Intent intent = new Intent(this, NetworkDetailsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(Commons.EXTRA_APINFO, info); + startActivityForResult(intent, RequestCode.DETAILS_SHOW); + + } + + private void launchCreditsActivity(){ + + Intent intent = new Intent(this, CreditsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + startActivityForResult(intent, RequestCode.NONE); + + } + + private void deleteNetwork(AccessPointInfo info){ + + NetworkManager manager = new NetworkManager(Commons.getNetworkStorageFile()); + String msg = ""; + if (manager.remove(info)){ + msg = "Network info deleted!"; + }else{ + msg = "FAILED to delete network info!"; + } + + Toast toast = Toast.makeText(this, msg, Toast.LENGTH_LONG); + toast.show(); + + } + + private void connectToNetwork(AccessPointInfo info){ + + Intent intent = new Intent(this, LongTaskActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(Commons.EXTRA_REQCODE, RequestCode.CONNECT); + intent.putExtra(Commons.EXTRA_APINFO, info); + startActivityForResult(intent, RequestCode.CONNECT); + + } + + private void checkConnectionStatus(){ + + Intent intent = new Intent(this, LongTaskActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(Commons.EXTRA_REQCODE, RequestCode.STATUS_GET); + startActivityForResult(intent, RequestCode.STATUS_GET); + + } + + private void doScan(){ + + Intent intent = new Intent(this, LongTaskActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(Commons.EXTRA_REQCODE, RequestCode.NETWORKS_GET); + startActivityForResult(intent, RequestCode.NETWORKS_GET); + + } + + public void btnScanClick(View v){ + + doScan(); + + } + + public void btnManageClick(View v){ + + NetworkManager manager = new NetworkManager(Commons.getNetworkStorageFile()); + AccessPointInfo[] infos = manager.getKnownNetworks(); + + if (infos == null || infos.length == 0){ + Toast toast = Toast.makeText(this, "No saved network", Toast.LENGTH_LONG); + toast.show(); + } + else{ + launchSelectActivity(infos, false); + } + + } + +} diff --git a/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java b/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java new file mode 100644 index 0000000..e43eee2 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java @@ -0,0 +1,145 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + +import java.util.Date; + + +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.AccessPointInfo; + +import android.os.Bundle; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.text.format.DateFormat; +import android.view.Menu; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; +import android.widget.CompoundButton.OnCheckedChangeListener; + +public class NetworkDetailsActivity extends Activity implements OnCheckedChangeListener { + + private AccessPointInfo currentNetwor; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_network_details); + + CheckBox c = (CheckBox)findViewById(R.id.chk_show_pass_details); + c.setOnCheckedChangeListener(this); + + Intent intent = getIntent(); + if (! intent.hasExtra(Commons.EXTRA_APINFO)){ + this.setResult(RESULT_CANCELED); + this.finish(); + return; + } + + this.currentNetwor = (AccessPointInfo)intent.getExtras().getSerializable(Commons.EXTRA_APINFO); + if (this.currentNetwor == null){ + this.setResult(RESULT_CANCELED); + this.finish(); + return; + } + + loadNetwork(false); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + //getMenuInflater().inflate(R.menu.activity_manage_networks, menu); + return true; + } + + private void loadNetwork(boolean showPassword){ + + setTitle(this.currentNetwor.getSSID()); + + TextView v = (TextView)findViewById(R.id.txt_net_details); + v.setText("SSID: " + this.currentNetwor.getSSID()); + v.append("\nBSSID: " + this.currentNetwor.getBSSID()); + + long lastused = this.currentNetwor.getLastTimeUsed(); + if (lastused > 0){ + Date ts = new Date(lastused); + String formstring = "dd-MMM-yyyy kk:mm:ss"; + v.append("\nLast Used: " + DateFormat.format(formstring, ts)); + } + + if (showPassword){ + v.append("\n\nPassword:\n" + this.currentNetwor.getPassword()); + }else{ + v.append("\n\n\n"); + } + + } + + public void btnDeleteClick(View v){ + + + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); + dlgAlert.setMessage("Are you sure you want to delete this network?"); + dlgAlert.setPositiveButton("Yes",new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + returnResult(true); + } + }); + dlgAlert.setNegativeButton("NO",new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + //nothing + } + }); + + dlgAlert.setCancelable(true); + dlgAlert.create().show(); + + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + + if (buttonView == findViewById(R.id.chk_show_pass_details)){ + loadNetwork(isChecked); + } + + } + + private void returnResult(boolean delete){ + + Intent i = new Intent(); + i.putExtra(Commons.EXTRA_DELETE, delete); + i.putExtra(Commons.EXTRA_APINFO, this.currentNetwor); + this.setResult(RESULT_OK,i); + finish(); + + } + + + +} diff --git a/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java b/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java new file mode 100644 index 0000000..275ce08 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java @@ -0,0 +1,222 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + + + + +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.AccessPointInfo; +import fil.libre.repwifiapp.helpers.NetworkButton; +import fil.libre.repwifiapp.helpers.Utils; + +import android.os.Bundle; +import android.app.Activity; +import android.content.Intent; +import android.view.Gravity; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup.LayoutParams; +import android.widget.Button; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +public class SelectNetworkActivity extends Activity implements OnClickListener { + + private AccessPointInfo[] aps; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_select_network); + + setTitle("Select network"); + + getNetworks(); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + //getMenuInflater().inflate(R.menu.activity_select_network, menu); + return true; + } + + private void writeOut(String msg) { + + TextView v = (TextView)findViewById(R.id.txt_selnets); + v.setText(msg); + + } + + private void getNetworks(){ + + Intent intent = getIntent(); + if(! intent.hasExtra(Commons.EXTRA_APINFO_ARR)){ + this.setResult(RESULT_CANCELED); + finish(); + return; + } + AccessPointInfo[] nets = (AccessPointInfo[])intent.getExtras().getSerializable(Commons.EXTRA_APINFO_ARR); + if (nets == null){ + this.setResult(RESULT_CANCELED); + finish(); + return; + } + + int reqCode = intent.getExtras().getInt(Commons.EXTRA_REQCODE); + + this.aps = nets; + + if (reqCode == Commons.RequestCode.SELECT_CONN){ + showNetworksForConnection(nets); + } + else{ + showNetworksForManagement(nets); + } + + } + + public void btnScanClick(View v){ + returnResults(null, true); + } + + @Override + public void onClick(View v) { + + if (v instanceof NetworkButton){ + networkNameClick((NetworkButton)v); + } + + } + + public void networkNameClick(NetworkButton b){ + + for(AccessPointInfo i : this.aps){ + + if (i.getBSSID().equals(b.getNetworkBSSID())){ + + returnResults(i,false); + + } + + } + } + + private void returnResults(AccessPointInfo i, boolean rescan){ + + Intent intent = new Intent(); + intent.putExtra(Commons.EXTRA_APINFO, i); + intent.putExtra(Commons.EXTRA_RESCAN, rescan); + setResult(RESULT_OK, intent); + finish(); + + } + + private void showNetworksForConnection(AccessPointInfo[] info) { + + if (info == null){ + Utils.logError("Unable to retrieve network list!"); + writeOut("Unable to retrieve network list!"); + return; + } + + if (info.length == 0){ + writeOut("No network found."); + toggleBtnRescan(true); + return; + } + + writeOut("Select the network you want to connect to:"); + toggleBtnRescan(false); + + for (AccessPointInfo i : info){ + + addButtonForNetwork(i); + + } + + } + + private void showNetworksForManagement(AccessPointInfo[] info){ + + if (info == null || info.length == 0){ + return; + } + + writeOut("Select network info to manage:"); + toggleBtnRescan(false); + + for (AccessPointInfo i : info){ + + addButtonForNetwork(i); + + } + + } + + private void toggleBtnRescan(boolean enable) { + + Button b = (Button)findViewById(R.id.btn_rescan); + if (enable){ + b.setVisibility(View.VISIBLE); + } + else{ + b.setVisibility(View.INVISIBLE); + } + } + + private void addButtonForNetwork(AccessPointInfo info){ + + TableLayout s = (TableLayout)findViewById(R.id.table_networks); + TableLayout.LayoutParams tableParams = new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT); + TableRow row = new TableRow(this); + TableRow.LayoutParams rowParams = new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + rowParams.gravity = Gravity.FILL_HORIZONTAL; + row.setPadding(10, 10, 10, 10); + row.setLayoutParams(rowParams); + row.setGravity(Gravity.FILL_HORIZONTAL); + row.setLayoutParams(rowParams); + + NetworkButton button = new NetworkButton(this, info.getBSSID()); + + TableRow.LayoutParams params = new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + button.setLayoutParams(params); + button.setBackgroundColor(Commons.colorThemeDark); + button.setTextColor(Commons.colorThemeLight); + button.setTextSize(20); + button.setPadding(10, 10, 10, 10); + button.setGravity(Gravity.CENTER_HORIZONTAL); + button.setText(info.getSSID()); + button.setOnClickListener(this); + + row.addView(button,params); + s.addView(row,tableParams); + s.setGravity(Gravity.FILL_HORIZONTAL); + + } + +} diff --git a/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java b/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java new file mode 100644 index 0000000..2674737 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java @@ -0,0 +1,131 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + + + +package fil.libre.repwifiapp.activities; + + +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.ConnectionStatus; +import fil.libre.repwifiapp.helpers.Utils; + +import android.os.Bundle; +import android.app.Activity; +import android.view.Menu; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +public class ShowStatusActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_show_status); + showStatus(); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + //getMenuInflater().inflate(R.menu.activity_show_status, menu); + return true; + } + + @Override + public void onRestart(){ + super.onRestart(); + showStatus(); + } + + private void setMessage(String msg){ + TextView view = (TextView)findViewById(R.id.txt_status); + view.setText(msg); + } + + private void showStatus(){ + + ConnectionStatus status = Commons.connectionEngine.getConnectionStatus(); + if (status == null){ + this.finish(); + + }else if (status.isConnected()){ + Utils.logDebug("StatusActivity isConnected,showing buttons"); + setMessage("Connected to " + status.SSID + "\n\n" + "IP Address: " + status.IP + "\n"); + toggleBtnDisconnect(true); + + + }else{ + Utils.logDebug("StatusActivity status Else"); + setMessage("Status:\n" + status.status); + toggleBtnDisconnect(false); + + } + } + + private void toggleBtnDisconnect(boolean enable){ + + Button b = (Button)findViewById(R.id.btn_disconnect); + Button bk = (Button)findViewById(R.id.btn_back); + b.setEnabled(enable); + bk.setEnabled(! enable); + + if (enable){ + b.setVisibility(View.VISIBLE); + bk.setVisibility(View.INVISIBLE); + }else{ + b.setVisibility(View.INVISIBLE); + bk.setVisibility(View.VISIBLE); + } + + } + + public void onBtnDisconnectClick(View v){ + + boolean res = Commons.connectionEngine.disconnect(); + String msg = ""; + if (res){ + msg = "Disconnected."; + } + else{ + msg = "FAILED to disconnect!"; + } + + Toast toast = Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT); + toast.show(); + + showStatus(); + + } + + public void onBtnMainClick(View v){ + finish(); + } + + @Override + public void onBackPressed() { + moveTaskToBack(true); + } + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/AccessPointInfo.java b/app/src/fil/libre/repwifiapp/helpers/AccessPointInfo.java new file mode 100644 index 0000000..65c8b24 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/AccessPointInfo.java @@ -0,0 +1,196 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import java.io.File; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import fil.libre.repwifiapp.Commons; + + +public class AccessPointInfo implements Serializable{ + + private static final long serialVersionUID = 1L; + + private String _ssid; + private String _bssid; + private String _auth; + private String _level; + private String _freq; + private String _password; + private long _lastTimeUsed; + + public AccessPointInfo(String ssid, String bssid, String authType, String level, String freq){ + + this._ssid = ssid; + this._bssid = bssid; + this._auth = authType; + this._level = level; + this._freq = freq; + + } + + public String getSSID(){ + return this._ssid; + } + + public String getBSSID(){ + return this._bssid; + } + + public String getAuthType(){ + return this._auth; + } + + public String getSignlalStrength(){ + return this._level; + } + + public String getFrequency(){ + return this._freq; + } + + public long getLastTimeUsed(){ + return this._lastTimeUsed; + } + + public void setLastTimeUsed(long timeStampInMillis){ + this._lastTimeUsed = timeStampInMillis; + } + + public boolean isOlderThan(int days){ + + if (this._lastTimeUsed == 0){ + return false; + } + + long timeDiff = System.currentTimeMillis() - this._lastTimeUsed; + long spanMillis = Utils.daysToMilliseconds(days); + + if (timeDiff > spanMillis){ + return true; + }else{ + return false; + } + + + } + + public String getPassword(){ + return this._password; + } + + public void setPassword(String password){ + this._password = password; + } + + public boolean needsPassword(){ + + if ( (this._auth == null) || (this._auth.equals("")) ){ + //TODO + //check if default behavior should be with or without password, + //when no auth info is available. + return false; + } + + if (this._auth.contains("WPA2") || this._auth.contains("WPA")){ + return true; + } + else { + return false; + } + + } + + protected static AccessPointInfo parseLine(String line){ + + try{ + + String[] params = line.split("\t"); + if (params.length != 5){ + return null; + } + + String bssid = params[0]; + String freq = params[1]; + String level = params[2]; + String auth = params[3]; + String ssid = params [4]; + + AccessPointInfo info = new AccessPointInfo(ssid, bssid, auth, level, freq); + return info; + + }catch (Exception e){ + Utils.logError("Error while parsing line: " + line, e); + return null; + } + + } + + public static AccessPointInfo[] parseScanResult(String scanResultFile){ + + try { + + File f = new File(scanResultFile); + if (! f.exists()){ + Utils.logError("AccessPointInfo.parseScanResult(): The provided scan result file doesn't exist"); + return null; + } + + String[] lines = Utils.readFileLines(Commons.getScanFile()); + List<AccessPointInfo> nets = new ArrayList<AccessPointInfo>(); + + for(String l : lines){ + if (l.startsWith(Commons.SCAN_FILE_HDR)){ + //strip off the header + continue; + } + + if (l.trim().equals("")){ + //empty line, skip. + continue; + } + + //try to parse line into network info + AccessPointInfo info = AccessPointInfo.parseLine(l); + if (info == null){ + Utils.logError("Failed to parse line into AccessPointInfo: " + l); + continue; + } + + nets.add(info); + + } + + AccessPointInfo[] a = new AccessPointInfo[nets.size()]; + a = nets.toArray(a); + return a; + + } catch (Exception e) { + Utils.logError("Error while parsing scan results in class AccessPointInfo",e); + return null; + } + + } + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java b/app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java new file mode 100644 index 0000000..f7cb8d7 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/ConnectionStatus.java @@ -0,0 +1,106 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import java.io.Serializable; + +public class ConnectionStatus implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 1L; + public static final String STATUS_CONNECTED = "COMPLETED"; + public static final String STATUS_INACTIVE = "INACTIVE"; + public static final String STATUS_DISCONNECTED = "DISCONNECTED"; + public static final String STATUS_UNDEFINED = "UNDEFINED"; + + public String status; + public String SSID; + public String BSSID; + public String IP; + + private static final String F_SEP = "="; + private static final String KeyStatus = "wpa_state"; + private static final String KeySSID = "ssid"; + private static final String KeyBSSID = "bssid"; + private static final String KeyIP = "ip_address"; + + public static ConnectionStatus parseWpaCliOutput(String wpaCliOutput){ + + if (wpaCliOutput == null){ + return null; + } + + if (wpaCliOutput.trim().length() == 0){ + return null; + } + + String[] lines = wpaCliOutput.split("\n"); + + ConnectionStatus s = new ConnectionStatus(); + for(String line : lines){ + + if (line.trim().equals("")){ + continue; + } + + String[] fields = line.split(F_SEP); + if(fields.length < 2){ + continue; + } + + String key = fields[0]; + String val = fields[1]; + + if (key.equals(KeyBSSID)){ + s.BSSID = val; + } + else if (key.equals(KeySSID)){ + s.SSID = val; + } + else if (key.equals(KeyStatus)){ + s.status = val; + } + else if (key.equals(KeyIP)){ + s.IP = val; + } + + } + + return s; + + } + + public boolean isConnected(){ + + if (this.status == null){ + return false; + } + + if (this.status.equals(STATUS_CONNECTED)){ + return true; + }else{ + return false; + } + } + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/Engine.java b/app/src/fil/libre/repwifiapp/helpers/Engine.java new file mode 100644 index 0000000..cdddb0b --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/Engine.java @@ -0,0 +1,397 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import fil.libre.repwifiapp.Commons; + + +public abstract class Engine implements IEngine{ + + protected String getCmdWpaSup(){ + return "wpa_supplicant -B -dd -i" + Commons.INTERFACE_NAME + " -C\"" +Commons.SOCKET_DIR + "\" -P\"" + Commons.PID_FILE + "\""; + } + + protected String getCmdWpaCli() { + return "wpa_cli -p" + Commons.SOCKET_DIR + " -P" + Commons.PID_FILE + " -i" + Commons.INTERFACE_NAME; + } + + protected abstract String getCmdWpaStart(); + + public static final String DNS1 = "193.183.98.154"; + public static final String DNS2 = "87.98.175.85"; + + public boolean deleteFileIfExists(String filePath){ + + if (filePath == null){ + return false; + } + + if (filePath.contains("*")){ + //it's safer to reject bulk rm'ing + return false; + } + + if (filePath.contains(" -r ")){ + //only file rm'ing acceppted + return false; + } + + return executeRootCmd("if [ -e \""+ filePath + "\" ]; then rm \"" + filePath + "\"; fi"); + } + + public boolean chmodFile(String filePath, String mod){ + return executeRootCmd("chmod " + mod + " \"" + filePath + "\""); + } + + @Override + public boolean killPreviousConnections() { + + Utils.logDebug("killing wpa_supplicant..:"); + if (executeRootCmd("killall -SIGINT wpa_supplicant")){ + Utils.logDebug("Killed wpa_supplicant"); + }else{ + Utils.logDebug("Wpa_supplicant NOT killed."); + } + + Utils.logDebug("killing dhcpcd.."); + if (executeRootCmd("killall -SIGINT dhcpcd")){ + Utils.logDebug("Killed dhcpcd"); + }else{ + Utils.logDebug("dhcpcd NOT killed."); + } + + + return true; + + } + + @Override + public boolean clearWorkingDir(){ + + Utils.logDebug("clearWorkingDir():"); + + if (executeRootCmd("rm -r " + Commons.SOCKET_DIR)){ + Utils.logDebug("removed socket dir"); + } + + if (executeRootCmd("rm " + Commons.ENTROPY_FILE)){ + Utils.logDebug("removed entropy file"); + } + + if (executeRootCmd("rm " + Commons.PID_FILE)){ + Utils.logDebug("removed pidfile"); + } + + if (executeRootCmd("rm " + Commons.SOFTAP_FILE)){ + Utils.logDebug("removed softap file"); + } + + if (executeRootCmd("rm " + Commons.WPA_CONF)){ + Utils.logDebug("removed wpa conf file"); + } + + if (executeRootCmd("rm " + Commons.P2P_CONF)){ + Utils.logDebug("removed p2p conf file"); + } + + + return true; + + } + + @Override + public boolean startWpaSupplicant(){ + + Utils.logDebug("startWpaSupplicant():"); + + if (executeRootCmd(getCmdWpaSup())){ + return true; + }else{ + Utils.logDebug("Failed to start wpa"); + return false; + } + + } + + @Override + public AccessPointInfo[] getAvailableNetworks(){ + + Utils.logDebug("getAvailableNetworks():"); + + killPreviousConnections(); + + if (! clearWorkingDir()){ + Utils.logError("Failed clearing dir"); + return null; + } + + if (! startWpaSupplicant()){ + Utils.logError("Failed starting wpa_supplicant"); + return null; + } + + if (! createScanScripts()){ + Utils.logError("Failed creating scripts"); + return null; + } + + if (! scanNetworks()){ + Utils.logError("failed scanning networks"); + return null; + } + + if (!getScanResults()){ + Utils.logError("failed getting scan results"); + return null; + } + + //chmod 666 scan_file to make it readable + if (!chmodFile(Commons.getScanFile(), "666")){ + Utils.logError("failed chmodding scan_file"); + return null; + } + + AccessPointInfo[] a = AccessPointInfo.parseScanResult(Commons.getScanFile()); + if (a == null){ + Utils.logError("Unable to parse scan file into AccessPointInfo array"); + } + + + return a; + + } + + @Override + public abstract boolean connect(AccessPointInfo info); + + @Override + public boolean disconnect(){ + + if (! isWpaSupplicantRunning()){ + return true; + } + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " disconnect"); + if (su.execute() == 0){ + String out = su.getOutput(); + if (out != null && out.trim().replace("\n", "").equals("OK")){ + return true; + }else { + return false; + } + } + else{ + return false; + } + + } catch (Exception e) { + Utils.logError("Error while enabling network", e); + return false; + } + } + + /*** + * returns null if unable to determine connection status for any reason. + */ + @Override + public ConnectionStatus getConnectionStatus(){ + + Utils.logDebug("called getConnecitonStatus()"); + if (! isWpaSupplicantRunning()){ + //wpa_supplicant is not running. + //unable to determin status. + Utils.logDebug("wpa not running, cannot get connection status."); + return null; + + } + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " status"); + if(su.execute() == 0){ + String out = su.getOutput(); + if (out == null || out.trim().equals("")){ + return null; + } + else { + return ConnectionStatus.parseWpaCliOutput(out); + } + } + else { + return null; + } + + } catch (Exception e) { + Utils.logError("Error while executing wpa_cli status", e); + return null; + } + + } + + public boolean runDhcpcd(){ + + return executeRootCmd("dhcpcd " + Commons.INTERFACE_NAME); + + } + + public boolean interfaceUp(){ + return executeRootCmd("ifconfig " + Commons.INTERFACE_NAME + " up"); + } + + protected boolean executeRootCmd(String cmd){ + + try { + + RootCommand c = new RootCommand(cmd); + if ( c.execute() == 0){ + return true; + }else { + return false; + } + + } catch (Exception e) { + Utils.logError("Error executing \"" + cmd + "\"",e); + return false; + } + } + + protected boolean isWpaSupplicantRunning(){ + + boolean retval = false; + + try { + + RootCommand su = new RootCommand("pidof wpa_supplicant"); + if (su.execute() == 0){ + + if (su.getOutput().trim().equals("")){ + retval = false; + }else{ + retval = true; + } + + }else { + retval = false; + } + + + } catch (Exception e) { + Utils.logError("Exception during isWpaSupplicantRunning()",e); + retval = false; + } + + return retval; + + } + + protected boolean scanNetworks(){ + + return executeRootCmd("bash " + Commons.getScriptScan()); + + } + + protected boolean getScanResults(){ + + return executeRootCmd("bash " + Commons.getScriptScanRes()); + + } + + protected boolean createScanScripts(){ + + try { + + String scan = getCmdWpaCli() + " scan\n" + + "if [ $? -ne 0 ]; then\n" + + "exit 1\n" + + "fi\n" + + "sleep 2s\n"; + + String scanRes = "if [ -e \"" + Commons.getScanFile() + "\" ]; then\n" + + " rm \"" + Commons.getScanFile() + "\"\n" + + "fi\n" + + getCmdWpaCli() + " scan_results > \""+ Commons.getScanFile() + "\"\n" + + "if [ $? -ne 0 ]; then\n" + + " exit 1\n" + + "fi\n" + + "sleep 1s\n"; + + + //Try to create and chmod script scan + /* executeRootCmd("echo > " + Commons.getSCRIPT_SCAN()); + chmodFile(Commons.getSCRIPT_SCAN(), "666");*/ + + + if (! Utils.writeFile(Commons.getScriptScan(),scan,true) ){ + + Exception e = Utils.getLastException(); + if (e != null){ + Utils.logError("Error while writing scan script.",e); + } + + return false; + } + + //Try to create and chmod script scanres + /*executeRootCmd("echo > " + Commons.getSCRIPT_SCANRES()); + chmodFile(Commons.getSCRIPT_SCANRES(), "666");*/ + + if (! Utils.writeFile(Commons.getScriptScanRes(),scanRes,true) ){ + + Exception e = Utils.getLastException(); + if (e != null){ + Utils.logError("Error while writing getScanResults script.",e); + } + + return false; + } + + + return true; + + } catch (Exception e) { + + Utils.logError("Error while creating the scanning script.",e); + return false; + } + + } + + /*protected boolean createDhcpcdScritp(){ + + String scriptDhcp = "dhcpcd "+ Commons.INTERFACE_NAME + "\n" + + "sleep 3s\n"; + + if (! Utils.writeFile(Commons.getScriptDhcpcd(),scriptDhcp,true) ){ + + Exception e = Utils.getLastException(); + if (e != null){ + Utils.logError("Error while writing dhcpcd script.",e); + } + + return false; + } + + return true; + + }*/ +} diff --git a/app/src/fil/libre/repwifiapp/helpers/Engine4p2.java b/app/src/fil/libre/repwifiapp/helpers/Engine4p2.java new file mode 100644 index 0000000..12d8da8 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/Engine4p2.java @@ -0,0 +1,131 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import fil.libre.repwifiapp.Commons; + + +public class Engine4p2 extends Engine{ + + @Override + protected String getCmdWpaStart(){ + return "wpa_supplicant -B -dd -i" + Commons.INTERFACE_NAME + " -C\"" + Commons.SOCKET_DIR + "\" -c\"" + Commons.WPA_CONF + "\" -P\"" + Commons.PID_FILE + "\""; + } + + public boolean loadModules(){ + try { + //TODO + //implement kernel modules loading + return true; + } catch (Exception e) { + Utils.logError("Error while loading kernel modules",e); + return false; + } + } + + @Override + public boolean connect(AccessPointInfo info){ + + killPreviousConnections(); + + if (info == null){ + Utils.logDebug("Engine's connect() received a null AccessPointInfo"); + return false; + } + + if (! createConfigFile(info)){ + return false; + } + + //launch wpa_supplicant specifying our custom configuration and the socket file + if (! executeRootCmd(getCmdWpaStart())){ + Utils.logError("wpa_supplicant connection command failed."); + return false; + } + + //negotiate DHCP lease + if (!runDhcpcd()){ + return false; + } + + //set DNS's + if (! executeRootCmd("setprop net.dns1 " + DNS1)){ + Utils.logError("setting dns1 failed"); + return false; + } + + if (! executeRootCmd("setprop net.dns2 " + DNS2)){ + Utils.logError("setting dns2 failed"); + return false; + } + + //TODO + //implement wpa_cli command to query wpa_supplicant's state + //in order to confirm that connection was successful. + + return true; + + } + + private boolean createConfigFile(AccessPointInfo info){ + + try { + + if (! deleteFileIfExists(Commons.WPA_CONF)){ + Utils.logError("Unable to remove wpa_supplicant.conf before writing it."); + return false; + } + + String configText = "ctrl_interface=DIR=" + Commons.SOCKET_DIR + "\n" + + "update_config=1\n" + + "network={\n"+ + " ssid=\"" + info.getSSID() + "\"\n"; + + if (info.needsPassword()){ + configText += " psk=\""+ info.getPassword() + "\"\n"; + }else { + configText += " key_mgmt=NONE\n"; + } + + configText += "}\n"; + + if ( ! Utils.writeFile(Commons.WPA_CONF, configText, true) ){ + Utils.logError("Unable to write wpa_supplicant.conf file!"); + return false; + } + + //chmod wpa_supplicant.conf, in order to make it accessible + if(chmodFile(Commons.WPA_CONF, "666")){ + return true; + }else { + Utils.logError("Unable to chmod wpa_supplicant.conf"); + return false; + } + + } catch (Exception e) { + Utils.logError("Error while creating wpa_supplicant.conf",e); + return false; + } + + } + + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/Engine6p0.java b/app/src/fil/libre/repwifiapp/helpers/Engine6p0.java new file mode 100644 index 0000000..24096d2 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/Engine6p0.java @@ -0,0 +1,360 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import fil.libre.repwifiapp.Commons; + + +public class Engine6p0 extends Engine{ + + @Override + protected String getCmdWpaStart(){ + return "wpa_supplicant -B -dd -i" + Commons.INTERFACE_NAME + " -C" + Commons.SOCKET_DIR + " -P" + Commons.PID_FILE + " -I" + Commons.OVERLAY_FILE + " -e" + Commons.ENTROPY_FILE; + } + + @Override + public boolean connect(AccessPointInfo info){ + + killPreviousConnections(); + + if (info == null){ + Utils.logDebug("Engine's connect() received a null AccessPointInfo"); + return false; + } + + //clear any previously set network + if (! destroyNetwork()){ + Utils.logDebug("Unable to ndc destroy network"); + return false; + } + + //clear interface's ip + if (! clearAddrs()){ + Utils.logDebug("Unable to ndc clearaddrs"); + return false; + } + + //bring up interface + if(! interfaceUp()){ + Utils.logDebug("Unable to bring up interface."); + return false; + } + + //launch wpa_supplicant specifying our custom configuration and the socket file + if (! executeRootCmd(getCmdWpaStart())){ + Utils.logDebug("Unable to run wpa start"); + return false; + } + + //create new network and get network id + String netID = createNetworkGetId(); + if (netID == null){ + Utils.logDebug("Unable to fetch network id"); + return false; + } + + //set network SSID + if (! setNetworkSSID(info.getSSID(), netID)){ + Utils.logDebug("Failed to set network ssid"); + return false; + } + + //set password (if any) + if(! setNetworkPSK(info, netID)){ + Utils.logDebug("Failed to set network psk"); + return false; + } + + // select the network we just created + if (! selectNetwork(netID)){ + Utils.logDebug("Unable to wpa_cli select network"); + return false; + } + + //enable the newtork + if (! enableNetwork(netID)){ + Utils.logDebug("Unable to wpa_cli enable_newtork"); + return false; + } + + //try to reassociate to Access Point + if (! reassociate()){ + Utils.logDebug("Unable to wpa_cli reassociate"); + return false; + } + + //get DHCP + Utils.logDebug("Attempt to run dhcpcd.."); + if (!runDhcpcd()){ + Utils.logDebug("Failed to run dhcpcd"); + return false; + } + + //try to fetch gateway + String gw = getGateway(); + if (gw == null || gw.trim().length() < 7){ + //failed to get gateway + Utils.logDebug("Failed to get gateway"); + return false; + } + + if (! executeRootCmd("ndc network create 1")){ + Utils.logDebug("Failed to wpa_cli network create 1 "); + return false; + } + + if (! executeRootCmd("ndc network interface add 1 " + Commons.INTERFACE_NAME)){ + Utils.logDebug("Failed to add interface."); + return false; + } + + // set route to gateway for all traffic + if (! executeRootCmd("ndc network route add 1 " + Commons.INTERFACE_NAME + " 0.0.0.0/0 " + gw)){ + Utils.logDebug("Failed to add route to gateway"); + return false; + } + + //set DNS + if (! executeRootCmd("ndc resolver setnetdns 1 " + " " + DNS1 + " " + DNS2)){ + Utils.logDebug("Failed to set DNS"); + return false; + } + + //use network + if (! executeRootCmd("ndc network default set 1")){ + Utils.logDebug("Failed to set network as default"); + return false; + } + + //TODO + //implement wpa_cli query for status + //in order to be sure that connection is extablished + + return true; + + + + } + + private String createNetworkGetId(){ + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " add_network"); + if(su.execute() == 0){ + String out = su.getOutput(); + if (out == null || out.trim().equals("")){ + return null; + } + else { + return out.replace("\n", ""); + } + } + else { + return null; + } + + } catch (Exception e) { + Utils.logError("Error while creating network", e); + return null; + } + + } + + private boolean destroyNetwork(){ + return executeRootCmd("ndc network destroy 1"); + } + + private boolean setNetworkSSID(String ssid, String networkID){ + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " set_network " + networkID + " ssid '\"" + ssid + "\"'" ); + if (su.execute() == 0){ + String out = su.getOutput(); + if (out != null && out.trim().replace("\n", "").equals("OK")){ + return true; + }else { + return false; + } + } + else{ + return false; + } + + } catch (Exception e) { + Utils.logError("Error while setting network SSID", e); + return false; + } + + } + + private boolean setNetworkPSK(AccessPointInfo info, String networkID){ + + try { + + String cmdSetPass = null; + if (info.needsPassword()){ + cmdSetPass = getCmdWpaCli() + " set_network " + networkID + " psk '\"" + info.getPassword() + "\"'"; + } + else{ + cmdSetPass = getCmdWpaCli() + " set_network " + networkID + " key_mgmt NONE"; + } + + RootCommand su = new RootCommand(cmdSetPass); + if (su.execute() == 0){ + String out = su.getOutput(); + if (out != null && out.trim().replace("\n", "").equals("OK")){ + return true; + }else { + return false; + } + } + else{ + return false; + } + + } catch (Exception e) { + Utils.logError("Error while setting network PSK", e); + return false; + } + + + } + + private boolean selectNetwork(String networkID){ + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " select_network " + networkID); + if (su.execute() == 0){ + String out = su.getOutput(); + if (out != null && out.trim().replace("\n", "").equals("OK")){ + return true; + }else { + return false; + } + } + else{ + return false; + } + + } catch (Exception e) { + Utils.logError("Error while selecting network", e); + return false; + } + + } + + private boolean enableNetwork(String networkID){ + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " enable_network " + networkID); + if (su.execute() == 0){ + String out = su.getOutput(); + if (out != null && out.trim().replace("\n", "").equals("OK")){ + return true; + }else { + return false; + } + } + else{ + return false; + } + + } catch (Exception e) { + Utils.logError("Error while enabling network", e); + return false; + } + + } + + private boolean reassociate(){ + + try { + + RootCommand su = new RootCommand(getCmdWpaCli() + " reassociate"); + if (su.execute() == 0){ + String out = su.getOutput(); + if (out != null && out.trim().replace("\n", "").equals("OK")){ + return true; + }else { + return false; + } + } + else{ + return false; + } + + } catch (Exception e) { + Utils.logError("Error while reassociating network", e); + return false; + } + + } + + private String getGateway(){ + + try { + + RootCommand su = new RootCommand("ip route show dev " + Commons.INTERFACE_NAME); + if (su.execute() != 0){ + Utils.logDebug("command failed show route"); + return null; + } + + //read command output + String out = su.getOutput(); + if (out == null){ + return null; + } + + String[] lines = out.split("\n"); + + for (String l : lines){ + + if (l.contains("default via")){ + + String[] f = l.split(" "); + if (f.length > 2){ + + //found route's address: + return f[2]; + + } + } + } + + return null; + + } catch (Exception e) { + Utils.logError("Error while trying to fetch route",e); + return null; + } + + } + + private boolean clearAddrs(){ + return executeRootCmd("ndc interface clearaddrs " + Commons.INTERFACE_NAME); + } + +}
\ No newline at end of file diff --git a/app/src/fil/libre/repwifiapp/helpers/IEngine.java b/app/src/fil/libre/repwifiapp/helpers/IEngine.java new file mode 100644 index 0000000..67ffb86 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/IEngine.java @@ -0,0 +1,41 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + + +public interface IEngine { + + public boolean startWpaSupplicant(); + + public boolean killPreviousConnections(); + + public boolean clearWorkingDir(); + + public AccessPointInfo[] getAvailableNetworks(); + + public boolean connect(AccessPointInfo info); + + public boolean disconnect(); + + public ConnectionStatus getConnectionStatus(); + + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/NetworkButton.java b/app/src/fil/libre/repwifiapp/helpers/NetworkButton.java new file mode 100644 index 0000000..9a7c523 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/NetworkButton.java @@ -0,0 +1,41 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import android.content.Context; +import android.widget.Button; + +public class NetworkButton extends Button{ + + private String _bssid = ""; + + public NetworkButton(Context context, String networkBSSID){ + super(context); + this._bssid = networkBSSID; + } + + public String getNetworkBSSID(){ + return this._bssid; + } + + + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/NetworkManager.java b/app/src/fil/libre/repwifiapp/helpers/NetworkManager.java new file mode 100644 index 0000000..bc59862 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/NetworkManager.java @@ -0,0 +1,288 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import java.io.File; +import java.util.ArrayList; + +public class NetworkManager { + + private static final String F_SEP = "\t"; + private static final int NET_MAX_AGE = 365; //Expressed in days + + private String _knownNetworksFile = null; + + public NetworkManager(String networksFilePath){ + this._knownNetworksFile = networksFilePath; + } + + private AccessPointInfo searchInFile(AccessPointInfo i){ + + if (i == null){ + return null; + } + + String bssid = i.getBSSID(); + String ssid = i.getSSID(); + + if (bssid == null || ssid == null || bssid.trim().equals("") || ssid.trim().equals("")){ + return null; + } + + AccessPointInfo ret = null; + AccessPointInfo[] list = getKnownNetworks(); + + if (list == null){ + return null; + } + + for(AccessPointInfo toTest : list){ + + // try to match both bssid and ssid. + // if bssid doesn't match, but ssid does, + // then the network is a candidate. + // if no bssid equality is found, + // then return the best match (only ssid), if any + if (toTest.getSSID().equals(ssid)){ + + if (toTest.getBSSID().equals(bssid)){ + i.setPassword(toTest.getPassword()); + return i; + + }else{ + i.setPassword(toTest.getPassword()); + ret = i; + } + + } + + } + + return ret; + + } + + private boolean saveOrRemove(AccessPointInfo info, boolean save){ + + String iText = InfoToString(info); + if (iText == null){ + return false; + } + + AccessPointInfo[] existingNets = getKnownNetworks(); + + ArrayList<AccessPointInfo> newlist = new ArrayList<AccessPointInfo>(); + + if (existingNets == null || existingNets.length == 0){ + //no existing storage yet, create it + + if (save){ + //set timestamp + info.setLastTimeUsed(System.currentTimeMillis()); + newlist.add(info); + AccessPointInfo[] newContents = new AccessPointInfo[newlist.size()]; + newContents = newlist.toArray(newContents); + + return saveList(newContents); + + }else{ + //nothing to do, return + return true; + } + + } + + for(AccessPointInfo old : existingNets){ + + if (old == null){ + continue; + } + + if (old.getBSSID().equals(info.getBSSID()) && old.getSSID().equals(info.getSSID())){ + + //found previous entry for this network, + //if the call is for saving, overwrite the entry with the new one, + //else omit the line, to remove network from the saved list. + if (save){ + info.setLastTimeUsed(System.currentTimeMillis()); + newlist.add(info); + } + + }else{ + //other network, keep it in the file + // only if it's not older than the max age for a network + if (! info.isOlderThan(NET_MAX_AGE)){ + newlist.add(old); + } + } + + } + + AccessPointInfo[] newContents = new AccessPointInfo[newlist.size()]; + newContents = newlist.toArray(newContents); + + return saveList(newContents); + + } + + private AccessPointInfo getFromString(String savedString){ + + if (savedString == null || savedString.trim().equals("")) { + return null; + } + + String[] fields = savedString.split(F_SEP); + + if (fields.length != 4 ){ + return null; + } + + String bssid = fields[0]; + String ssid = fields[1]; + String pass = fields[2]; + String lastUsed = fields[3]; + + long lastusedmillis = 0; + try { + lastusedmillis = Long.parseLong(lastUsed); + } catch (NumberFormatException e) { + //invalid format + Utils.logError("Invalid time format in network manager \""+lastUsed +"\". Network BSSID: " + bssid, e); + } + + if (bssid.trim().equals("") || ssid.trim().equals("") || pass.trim().equals("")){ + return null; + } + + AccessPointInfo i = new AccessPointInfo(ssid, bssid, null, null, null); + i.setPassword(pass); + i.setLastTimeUsed(lastusedmillis); + + return i; + + } + + private String InfoToString(AccessPointInfo info){ + + if (info == null){ + return null; + } + + String bssid = info.getBSSID(); + String ssid = info.getSSID(); + String pass = info.getPassword(); + String tsLastUsed = "" + info.getLastTimeUsed(); + + if (bssid == null || bssid.trim().equals("")){ + return null; + } + + if (ssid == null || ssid.trim().equals("")){ + return null; + } + + if (pass == null || pass.trim().equals("")){ + return null; + } + + String iText = info.getBSSID() + F_SEP + info.getSSID() + F_SEP + info.getPassword() + F_SEP + tsLastUsed; + return iText; + + } + + private boolean saveList(AccessPointInfo[] list){ + + if (list == null){ + return false; + } + + String[] lines = new String[list.length]; + + for (int i = 0; i<list.length; i++){ + + String storeString = InfoToString(list[i]); + if (storeString == null){ + return false; + } + lines[i] = storeString; + + } + + return Utils.writeFileLines(this._knownNetworksFile,lines, true); + + } + + public AccessPointInfo[] getKnownNetworks(){ + + ArrayList<AccessPointInfo> list = new ArrayList<AccessPointInfo>(); + + File f = new File(this._knownNetworksFile); + if (! f.exists()){ + return null; + } + + String[] lines = Utils.readFileLines(_knownNetworksFile); + if (lines.length == 0){ + return null; + } + + for(String l : lines){ + + AccessPointInfo info = getFromString(l); + if (info != null){ + list.add(info); + } + + } + + AccessPointInfo[] ret = new AccessPointInfo[list.size()]; + ret = list.toArray(ret); + + return ret; + + } + + public boolean isKnown(AccessPointInfo info){ + + AccessPointInfo i = searchInFile(info); + if (i == null){ + return false; + }else { + return true; + } + + } + + public boolean save(AccessPointInfo info){ + return saveOrRemove(info, true); + } + + public boolean remove(AccessPointInfo info){ + return saveOrRemove(info, false); + } + + public AccessPointInfo getSavedNetwork(AccessPointInfo i){ + return searchInFile(i); + } + + + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/RootCommand.java b/app/src/fil/libre/repwifiapp/helpers/RootCommand.java new file mode 100644 index 0000000..3da4ae0 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/RootCommand.java @@ -0,0 +1,109 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; + +import fil.libre.repwifiapp.Commons; + +public class RootCommand { + + private String _cmdOut = ""; + private String _cmdTxt = ""; + + public RootCommand(String commandText){ + this._cmdTxt = commandText; + } + + public int execute() throws Exception{ + return execute(0); + } + + public int execute(int sleepSecsAfterCmd) throws Exception{ + + Process su = Runtime.getRuntime().exec("su"); + + DataOutputStream stdin = new DataOutputStream(su.getOutputStream()); + InputStream os = su.getInputStream(); + BufferedReader stdOut = new BufferedReader(new InputStreamReader(os)); + InputStream es = su.getErrorStream(); + BufferedReader stdError = new BufferedReader(new InputStreamReader(es)); + + if ( this._cmdTxt != null ){ + + Utils.logDebug("EXEC: " + this._cmdTxt); + + this._cmdTxt += " > " + Commons.getTempOutFile(); + + stdin.writeBytes(this._cmdTxt + "\n"); + stdin.flush(); + } + + /* if (sleepSecsAfterCmd > 0){ + Thread.sleep(sleepSecsAfterCmd * 1000); + }*/ + + StringBuilder sb = new StringBuilder(); + String s = null; + + while ( (es.available() > 0) && (s = stdError.readLine()) != null) { + sb.append(s + "\n"); + } + + while ( (os.available() > 0) && (s = stdOut.readLine()) != null) { + sb.append(s + "\n"); + } + + this._cmdOut = sb.toString(); + + stdin.writeBytes("exit\n"); + stdin.flush(); + + int res = su.waitFor(); + + Utils.logDebug("OUT: " + getOutput()); + + return res; + + } + + public String getOutput(){ + + String[] lastOut = Utils.readFileLines(Commons.getTempOutFile()); + if (lastOut == null){ + return this._cmdOut; + } + + String fout = ""; + + for (String s : lastOut){ + fout += s + "\n"; + } + + return fout; + + } + + + +} diff --git a/app/src/fil/libre/repwifiapp/helpers/Utils.java b/app/src/fil/libre/repwifiapp/helpers/Utils.java new file mode 100644 index 0000000..2e5fa27 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/helpers/Utils.java @@ -0,0 +1,205 @@ +// +// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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 3 of the License, or +// (at your option) any later version. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.helpers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +public class Utils { + + private static final long MILLIS_IN_DAY = 86400000; + + public static final int logLevel = 1; + + public static final String APP_NAME = "RepWifi"; + + private static Exception _lastException = null; + + public static Exception getLastException(){ + return _lastException; + } + + public static void logError(String msg, Exception e){ + Log.e(APP_NAME,msg,e); + } + + public static void logError(String msg){ + Log.e(APP_NAME,msg); + } + + public static void logDebug(String msg){ + logDebug(msg,0); + } + + public static void logDebug(String msg, int level){ + + if (level < logLevel){ + return; + } + + Log.d(APP_NAME,msg); + } + + public static boolean writeFile(String filePath, String text, boolean overwrite){ + + + FileWriter writer = null; + boolean retval = false; + try { + + writer = new FileWriter(filePath, (! overwrite)); + writer.write(text); + + retval = true; + + } catch (Exception e) { + _lastException = e; + retval = false; + } + finally{ + + if (writer != null){ + try { + writer.close(); + } catch (IOException e) { + //suppress + } + } + + } + + return retval; + + } + + public static boolean writeFileLines(String filePath, String[] lines, boolean overwrite){ + + if (lines == null){ + return false; + } + + FileWriter writer = null; + boolean retval = false; + try { + + writer = new FileWriter(filePath, (! overwrite)); + + if (lines.length == 0){ + writer.write(""); + } + + for(String l : lines){ + writer.write(l + "\n"); + } + + retval = true; + + } catch (Exception e) { + _lastException = e; + retval = false; + } + finally{ + + if (writer != null){ + try { + writer.close(); + } catch (IOException e) { + //suppress + } + } + + } + + return retval; + + } + + public static String[] readFileLines(String filePath){ + + if (filePath == null){ + return null; + } + + File f = new File(filePath); + if (! f.exists()){ + logError("File doesn't exist: " + filePath); + return null; + } + + FileReader fr = null; + BufferedReader bufr = null; + + List<String> lines = new ArrayList<String>(); + String[] ret = null; + + try { + + fr = new FileReader(filePath); + bufr = new BufferedReader(fr); + String line =""; + while((line = bufr.readLine()) != null){ + lines.add(line); + } + + String[] ar = new String[lines.size()]; + ret = lines.toArray(ar); + + } catch (Exception e) { + logError("Error while reading file " + filePath,e); + ret = null; + } + finally{ + try { + if (bufr != null){ + bufr.close(); + } + } catch (IOException ex) { + //suppress + } + try { + if (fr != null){ + fr.close(); + } + }catch(IOException exc){ + //suppress + } + } + + return ret; + + } + + public static long daysToMilliseconds(int days){ + return (days * MILLIS_IN_DAY); + } + + public static long millisecondsToDays(long milliseconds){ + return (milliseconds / MILLIS_IN_DAY); + } + +} |