├── settings.gradle ├── play ├── assets │ ├── screen1.png │ ├── screen2.png │ ├── screen3.png │ ├── screen4.png │ ├── screen5.png │ ├── screen6.png │ ├── ic_launcher-hdpi.png │ ├── ic_launcher-hires.png │ ├── ic_launcher-mdpi.png │ ├── ic_launcher-xhdpi.png │ ├── ic_launcher-feature.png │ └── ic_launcher-xxhdpi.png └── releases │ ├── PortKnocker-release-1.0.1.apk │ ├── PortKnocker-release-1.0.2.apk │ ├── PortKnocker-release-1.0.3.apk │ ├── PortKnocker-release-1.0.4.apk │ ├── PortKnocker-release-1.0.5.apk │ ├── PortKnocker-release-1.0.6.apk │ ├── PortKnocker-release-1.0.7.apk │ └── PortKnocker-release-1.0.apk ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── app ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable-hdpi │ │ │ │ ├── ic_alert.png │ │ │ │ ├── fab_ic_add.png │ │ │ │ ├── ic_delete.png │ │ │ │ ├── ic_pencil.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_content_save.png │ │ │ │ ├── ic_sort_variant.png │ │ │ │ ├── ic_divider_dashed.png │ │ │ │ └── appwidget_dark_bg.9.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── ic_alert.png │ │ │ │ ├── fab_ic_add.png │ │ │ │ ├── ic_delete.png │ │ │ │ ├── ic_pencil.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_content_save.png │ │ │ │ ├── ic_sort_variant.png │ │ │ │ └── appwidget_dark_bg.9.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── ic_alert.png │ │ │ │ ├── ic_delete.png │ │ │ │ ├── ic_pencil.png │ │ │ │ ├── fab_ic_add.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_content_save.png │ │ │ │ ├── ic_sort_variant.png │ │ │ │ └── appwidget_dark_bg.9.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── ic_alert.png │ │ │ │ ├── fab_ic_add.png │ │ │ │ ├── ic_delete.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_pencil.png │ │ │ │ ├── ic_content_save.png │ │ │ │ └── ic_sort_variant.png │ │ │ ├── drawable │ │ │ │ ├── widget_preview_1x1.png │ │ │ │ ├── widget_preview_2x1.png │ │ │ │ ├── widget_preview_3x1.png │ │ │ │ ├── widget_preview_4x1.png │ │ │ │ └── selectable_item_background.xml │ │ │ ├── xml │ │ │ │ ├── file_provider_paths.xml │ │ │ │ ├── widget1x1.xml │ │ │ │ ├── widget2x1.xml │ │ │ │ ├── widget3x1.xml │ │ │ │ ├── widget4x1.xml │ │ │ │ └── preferences.xml │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── layout │ │ │ │ ├── toolbar.xml │ │ │ │ ├── host_list.xml │ │ │ │ ├── settings.xml │ │ │ │ ├── icon_text_item.xml │ │ │ │ ├── host_edit.xml │ │ │ │ ├── ports_header.xml │ │ │ │ ├── widget.xml │ │ │ │ ├── host_fragment.xml │ │ │ │ ├── port_row.xml │ │ │ │ ├── list_view.xml │ │ │ │ ├── host_row.xml │ │ │ │ └── misc_fragment.xml │ │ │ ├── menu │ │ │ │ └── host_list.xml │ │ │ ├── values-ja │ │ │ │ └── strings.xml │ │ │ └── values-de │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── xargsgrep │ │ │ │ └── portknocker │ │ │ │ ├── widget │ │ │ │ ├── HostWidget1x1.java │ │ │ │ ├── HostWidget2x1.java │ │ │ │ ├── HostWidget3x1.java │ │ │ │ ├── HostWidget4x1.java │ │ │ │ ├── ConfigureWidgetActivity.java │ │ │ │ ├── ConfigureWidgetHostArrayAdapter.java │ │ │ │ └── HostWidget.java │ │ │ │ ├── utils │ │ │ │ ├── BundleUtils.java │ │ │ │ ├── SocketUtils.java │ │ │ │ ├── StringUtils.java │ │ │ │ └── SerializationUtils.java │ │ │ │ ├── filter │ │ │ │ ├── FilenameInputFilter.java │ │ │ │ └── HostnameInputFilter.java │ │ │ │ ├── model │ │ │ │ ├── Application.java │ │ │ │ ├── Port.java │ │ │ │ └── Host.java │ │ │ │ ├── fragment │ │ │ │ ├── SettingsFragment.java │ │ │ │ ├── ProgressDialogFragment.java │ │ │ │ ├── PortsFragment.java │ │ │ │ ├── HostFragment.java │ │ │ │ └── HostListFragment.java │ │ │ │ ├── activity │ │ │ │ ├── SettingsActivity.java │ │ │ │ └── SettingsActivityCompat.java │ │ │ │ ├── adapter │ │ │ │ ├── EditHostFragmentPagerAdapter.java │ │ │ │ ├── ApplicationArrayAdapter.java │ │ │ │ ├── HostArrayAdapter.java │ │ │ │ └── PortArrayAdapter.java │ │ │ │ ├── asynctask │ │ │ │ ├── RetrieveApplicationsAsyncTask.java │ │ │ │ ├── KnockerAsyncTask.java │ │ │ │ └── Knocker.java │ │ │ │ └── db │ │ │ │ └── DatabaseHelper.java │ │ └── AndroidManifest.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── xargsgrep │ │ └── portknocker │ │ └── ApplicationTest.java ├── proguard-rules.pro └── build.gradle ├── FileChooser ├── src │ └── main │ │ ├── res │ │ ├── drawable-hdpi │ │ │ ├── ic_file.png │ │ │ ├── ic_chooser.png │ │ │ ├── ic_folder.png │ │ │ └── ic_provider.png │ │ ├── drawable-mdpi │ │ │ ├── ic_file.png │ │ │ ├── ic_chooser.png │ │ │ ├── ic_folder.png │ │ │ └── ic_provider.png │ │ ├── drawable-xhdpi │ │ │ ├── ic_file.png │ │ │ ├── ic_folder.png │ │ │ ├── ic_chooser.png │ │ │ └── ic_provider.png │ │ ├── drawable-xxhdpi │ │ │ ├── ic_file.png │ │ │ ├── ic_chooser.png │ │ │ ├── ic_folder.png │ │ │ └── ic_provider.png │ │ ├── values-v19 │ │ │ └── bool.xml │ │ ├── values │ │ │ ├── bool.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-ko │ │ │ └── strings.xml │ │ ├── values-de │ │ │ └── strings.xml │ │ ├── values-ru │ │ │ └── strings.xml │ │ ├── values-pl │ │ │ └── strings.xml │ │ ├── values-ca │ │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ │ └── strings.xml │ │ ├── values-fr │ │ │ └── strings.xml │ │ ├── values-es │ │ │ └── strings.xml │ │ ├── values-it │ │ │ └── strings.xml │ │ ├── values-ga │ │ │ └── strings.xml │ │ ├── values-v11 │ │ │ └── strings.xml │ │ ├── layout │ │ │ └── file.xml │ │ ├── values-ja │ │ │ └── strings.xml │ │ └── xml │ │ │ └── mimetypes.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── ipaulpro │ │ └── afilechooser │ │ ├── FileListAdapter.java │ │ ├── FileLoader.java │ │ ├── FileListFragment.java │ │ └── FileChooserActivity.java └── build.gradle ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':FileChooser' 2 | -------------------------------------------------------------------------------- /play/assets/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/screen1.png -------------------------------------------------------------------------------- /play/assets/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/screen2.png -------------------------------------------------------------------------------- /play/assets/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/screen3.png -------------------------------------------------------------------------------- /play/assets/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/screen4.png -------------------------------------------------------------------------------- /play/assets/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/screen5.png -------------------------------------------------------------------------------- /play/assets/screen6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/screen6.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /play/assets/ic_launcher-hdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/ic_launcher-hdpi.png -------------------------------------------------------------------------------- /play/assets/ic_launcher-hires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/ic_launcher-hires.png -------------------------------------------------------------------------------- /play/assets/ic_launcher-mdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/ic_launcher-mdpi.png -------------------------------------------------------------------------------- /play/assets/ic_launcher-xhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/ic_launcher-xhdpi.png -------------------------------------------------------------------------------- /play/assets/ic_launcher-feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/ic_launcher-feature.png -------------------------------------------------------------------------------- /play/assets/ic_launcher-xxhdpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/assets/ic_launcher-xxhdpi.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_alert.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/ic_alert.png -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.1.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.2.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.2.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.3.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.3.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.4.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.4.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.5.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.5.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.6.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.6.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.7.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.7.apk -------------------------------------------------------------------------------- /play/releases/PortKnocker-release-1.0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/play/releases/PortKnocker-release-1.0.apk -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/fab_ic_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/fab_ic_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_pencil.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/fab_ic_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/fab_ic_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/ic_pencil.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/ic_alert.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/ic_pencil.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/ic_alert.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/fab_ic_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/fab_ic_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/fab_ic_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/fab_ic_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/ic_pencil.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_preview_1x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable/widget_preview_1x1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_preview_2x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable/widget_preview_2x1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_preview_3x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable/widget_preview_3x1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_preview_4x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable/widget_preview_4x1.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-hdpi/ic_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-hdpi/ic_file.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-mdpi/ic_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-mdpi/ic_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_content_save.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_sort_variant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_sort_variant.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/ic_content_save.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_sort_variant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/ic_sort_variant.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-hdpi/ic_chooser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-hdpi/ic_chooser.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-hdpi/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-hdpi/ic_folder.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-mdpi/ic_chooser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-mdpi/ic_chooser.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-mdpi/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-mdpi/ic_folder.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xhdpi/ic_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xhdpi/ic_file.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xhdpi/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xhdpi/ic_folder.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xxhdpi/ic_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xxhdpi/ic_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_divider_dashed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/ic_divider_dashed.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/ic_content_save.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_sort_variant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/ic_sort_variant.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/ic_content_save.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_sort_variant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xxhdpi/ic_sort_variant.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | *.iml 4 | local.properties 5 | build 6 | app/app-release.apk 7 | app/manifest-merger-release-report.txt 8 | app/release 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-hdpi/ic_provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-hdpi/ic_provider.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-mdpi/ic_provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-mdpi/ic_provider.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xhdpi/ic_chooser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xhdpi/ic_chooser.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xhdpi/ic_provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xhdpi/ic_provider.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xxhdpi/ic_chooser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xxhdpi/ic_chooser.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xxhdpi/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xxhdpi/ic_folder.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/appwidget_dark_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-hdpi/appwidget_dark_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/appwidget_dark_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-mdpi/appwidget_dark_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/appwidget_dark_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/app/src/main/res/drawable-xhdpi/appwidget_dark_bg.9.png -------------------------------------------------------------------------------- /FileChooser/src/main/res/drawable-xxhdpi/ic_provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xargsgrep/PortKnocker/HEAD/FileChooser/src/main/res/drawable-xxhdpi/ic_provider.png -------------------------------------------------------------------------------- /app/src/main/res/xml/file_provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-v19/bool.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | true 6 | 7 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values/bool.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | false 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #37474F 5 | 6 | #263238 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 30 14:55:40 CEST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 빈 디렉토리 5 | 저장소가 제거되었습니다. 6 | 파일 선택 7 | 파일 선택 오류 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Leerer Ordner 4 | Speicher wurde entferntet. 5 | Wähle eine Datei 6 | Fehler beim Öffnen der Datei 7 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Пустая папка 5 | Storage was removed or unmounted. 6 | Выберите файл 7 | Ошибка при выборе файла 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-pl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pusty katalog 5 | Pamięć została usunięta lub odmontowana. 6 | Wybierz plik 7 | Błąd, podczas wybierania pliku 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-ca/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Carpeta buida 5 | S\'ha tret o desmuntat l\'emmagatzematge. 6 | Seleccioneu un fitxer 7 | Error en seleccionar el fitxer 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pasta Vazia 5 | Unidade externa removida ou não preparada. 6 | Selecione um Arquivo 7 | Erro ao selecionar o Arquivo 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dossier vide 5 | Le stockage a été enlevé ou démonté. 6 | Sélectionnez un fichier 7 | Erreur lors de la sélection du fichier 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Directorio vacío 5 | Se ha retirado o desmontado el almacenamiento. 6 | Seleccione un archivo 7 | Error al seleccionar el archivo 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Directory vuota 5 | Lo spazio di archiviazione è stato rimosso o smontato. 6 | Selezionare un file 7 | Errore nel selezionare il File 8 | 9 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-ga/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Comhadlann fholamh 5 | Baineadh amach an gléas stórála nó dínascadh é. 6 | Roghnaigh comhad 7 | Tharla botún fad is a bhí comhad á roghnú 8 | 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xargsgrep/portknocker/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.xargsgrep.portknocker; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase 10 | { 11 | public ApplicationTest() 12 | { 13 | super(Application.class); 14 | } 15 | } -------------------------------------------------------------------------------- /FileChooser/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | 6 | defaultConfig { 7 | minSdkVersion 19 8 | targetSdkVersion 25 9 | } 10 | 11 | buildTypes { 12 | release { 13 | minifyEnabled false 14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | implementation 'com.android.support:support-v4:25.4.0' 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 56dp 7 | 16dp 8 | 16dp 9 | 1dp 10 | 8dp 11 | -------------------------------------------------------------------------------- /app/src/main/res/xml/widget1x1.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/widget2x1.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/widget3x1.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/widget4x1.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selectable_item_background.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/host_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/icon_text_item.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/arabbani/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -dontwarn com.fasterxml.jackson.databind.ext.DOMSerializer 20 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-v11/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | Choose a file 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/HostWidget1x1.java: -------------------------------------------------------------------------------- 1 | package com.xargsgrep.portknocker.widget; 2 | 3 | /* 4 | * Copyright 2016 TDFKAOlli 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | public class HostWidget1x1 extends HostWidget { 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/HostWidget2x1.java: -------------------------------------------------------------------------------- 1 | package com.xargsgrep.portknocker.widget; 2 | 3 | /* 4 | * Copyright 2016 TDFKAOlli 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | public class HostWidget2x1 extends HostWidget { 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/HostWidget3x1.java: -------------------------------------------------------------------------------- 1 | package com.xargsgrep.portknocker.widget; 2 | 3 | /* 4 | * Copyright 2016 TDFKAOlli 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | public class HostWidget3x1 extends HostWidget { 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/HostWidget4x1.java: -------------------------------------------------------------------------------- 1 | package com.xargsgrep.portknocker.widget; 2 | 3 | /* 4 | * Copyright 2016 TDFKAOlli 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | public class HostWidget4x1 extends HostWidget { 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 0dp 19 | 16dp 20 | -------------------------------------------------------------------------------- /FileChooser/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /FileChooser/src/main/res/layout/file.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/utils/BundleUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.utils; 17 | 18 | import android.os.Bundle; 19 | 20 | public class BundleUtils 21 | { 22 | public static boolean contains(Bundle bundle, String key) 23 | { 24 | return (bundle != null && bundle.containsKey(key)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 空のディレクトリ 19 | ストレージが取り外されたか、マウント解除されました。 20 | ファイルを選択 21 | ファイルの選択時にエラー 22 | 内部ストレージ 23 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | Empty Directory 19 | Storage was removed or unmounted. 20 | Select a file 21 | Error selecting File 22 | Internal storage 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/host_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | 6 | defaultConfig { 7 | applicationId "com.xargsgrep.portknocker" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 14 11 | versionName "1.0.13" 12 | } 13 | signingConfigs { 14 | release { 15 | storeFile file("~/.android/android-release.keystore") 16 | keyAlias "android" 17 | storePassword System.getenv("KSTOREPWD") 18 | keyPassword System.getenv("KEYPWD") 19 | } 20 | } 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | packagingOptions { 28 | exclude 'META-INF/LICENSE' 29 | exclude 'META-INF/NOTICE' 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation project(':FileChooser') 35 | 36 | implementation fileTree(dir: 'libs', include: ['*.jar']) 37 | implementation 'com.android.support:appcompat-v7:25.4.0' 38 | implementation 'com.fasterxml.jackson.core:jackson-databind:2.5.1' 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/utils/SocketUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.utils; 17 | 18 | import java.io.IOException; 19 | import java.net.DatagramSocket; 20 | import java.net.Socket; 21 | 22 | public class SocketUtils 23 | { 24 | public static void closeQuietly(Socket socket) 25 | { 26 | try 27 | { 28 | if (socket != null && socket.isConnected()) socket.close(); 29 | } 30 | catch (IOException e) { } 31 | } 32 | 33 | public static void closeQuietly(DatagramSocket socket) 34 | { 35 | if (socket != null && socket.isConnected()) socket.close(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/filter/FilenameInputFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.filter; 17 | 18 | import android.text.InputFilter; 19 | import android.text.Spanned; 20 | 21 | public class FilenameInputFilter implements InputFilter 22 | { 23 | @Override 24 | public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) 25 | { 26 | for (int i = start; i < end; i++) 27 | { 28 | char c = source.charAt(i); 29 | if (!Character.isLetterOrDigit(c) && c != '.' && c != '-' && c != '_') return ""; 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/filter/HostnameInputFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.filter; 17 | 18 | import android.text.InputFilter; 19 | import android.text.Spanned; 20 | 21 | public class HostnameInputFilter implements InputFilter 22 | { 23 | @Override 24 | public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) 25 | { 26 | for (int i = start; i < end; i++) 27 | { 28 | char c = source.charAt(i); 29 | if (!Character.isLetterOrDigit(c) && c != '.' && c != '-' && c != ':') return ""; 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.utils; 17 | 18 | public class StringUtils 19 | { 20 | public static boolean contains(String stringToCheck, String str) 21 | { 22 | return (stringToCheck == null) ? false : stringToCheck.contains(str); 23 | } 24 | 25 | // String.isEmpty only available starting API 9 26 | public static boolean isEmpty(String str) 27 | { 28 | return (str == null || str.length() == 0); 29 | } 30 | 31 | public static boolean isBlank(String str) 32 | { 33 | return (str == null || isEmpty(str.trim())); 34 | } 35 | 36 | public static boolean isNotBlank(String str) 37 | { 38 | return !isBlank(str); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ports_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 29 | 30 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Port Knocker 2 | 3 | ## Description 4 | A basic port knocker client for Android. Similar to the other port knocker apps but with a nicer interface. Also includes a widget and the ability to choose an application to launch after knocking. 5 | 6 | 7 | Get it on F-Droid 8 | 9 | Get it on Google Play 10 | 11 | ## Usage 12 | First create a host. You must specify a label, hostname and at least one port. The delay (in ms) is the amount of time it will wait in between sending packets. The default value of 1000ms is recommended to prevent the host from receiving the packets out of sequence. You can optionally choose an app to launch after the knock is complete. Once the host has been created you can initiate the knock sequence by simply clicking on the host. The same thing applies to the widget. 13 | 14 | ## Privacy 15 | This app does not track, collect, or share any data it comes across with anyone or anything. There are no ads or analytics trackers in this software. It does not access, store, nor transmit any user data. Only data entered into the application is stored within the application on this device and is not stored outside of this device. 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 24 | 25 | 32 | 33 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/model/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.model; 17 | 18 | import android.graphics.drawable.Drawable; 19 | 20 | public class Application 21 | { 22 | private String label; 23 | private Drawable icon; 24 | private String intent; 25 | 26 | public Application(String label, Drawable icon, String intent) 27 | { 28 | this.label = label; 29 | this.icon = icon; 30 | this.intent = intent; 31 | } 32 | 33 | public Drawable getIcon() 34 | { 35 | return icon; 36 | } 37 | 38 | public void setIcon(Drawable icon) 39 | { 40 | this.icon = icon; 41 | } 42 | 43 | public String getLabel() 44 | { 45 | return label; 46 | } 47 | 48 | public void setLabel(String label) 49 | { 50 | this.label = label; 51 | } 52 | 53 | public String getIntent() 54 | { 55 | return intent; 56 | } 57 | 58 | public void setIntent(String intent) 59 | { 60 | this.intent = intent; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/res/menu/host_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 17 | 22 | 27 | 28 | 33 | 34 | 39 | 44 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/fragment/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.fragment; 17 | 18 | import android.content.SharedPreferences; 19 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 20 | import android.os.Bundle; 21 | import android.preference.PreferenceFragment; 22 | 23 | import com.xargsgrep.portknocker.R; 24 | import com.xargsgrep.portknocker.widget.HostWidget; 25 | 26 | public class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener 27 | { 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) 30 | { 31 | super.onCreate(savedInstanceState); 32 | addPreferencesFromResource(R.xml.preferences); 33 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 34 | } 35 | 36 | @Override 37 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) 38 | { 39 | if (isAdded() && getString(R.string.pref_key_hide_ports_widget).equals(key)) 40 | { 41 | HostWidget.updateAllAppWidgets(getActivity()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/host_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 31 | 32 | 40 | 41 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/model/Port.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.model; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; 19 | 20 | public class Port 21 | { 22 | private long hostId; 23 | private int index; 24 | private int port = -1; 25 | private Protocol protocol = Protocol.TCP; 26 | 27 | public static enum Protocol {TCP, UDP} 28 | 29 | public Port() { } 30 | 31 | @JsonIgnore 32 | public long getHostId() 33 | { 34 | return hostId; 35 | } 36 | 37 | public void setHostId(long hostId) 38 | { 39 | this.hostId = hostId; 40 | } 41 | 42 | @JsonIgnore 43 | public int getIndex() 44 | { 45 | return index; 46 | } 47 | 48 | public void setIndex(int index) 49 | { 50 | this.index = index; 51 | } 52 | 53 | public int getPort() 54 | { 55 | return port; 56 | } 57 | 58 | public void setPort(int port) 59 | { 60 | this.port = port; 61 | } 62 | 63 | public Protocol getProtocol() 64 | { 65 | return protocol; 66 | } 67 | 68 | public void setProtocol(Protocol protocol) 69 | { 70 | this.protocol = protocol; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/res/layout/port_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 31 | 32 | 37 | 38 | 39 | 45 | 46 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 26 | 27 | 40 | 41 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/activity/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.activity; 17 | 18 | import android.content.Intent; 19 | import android.os.Bundle; 20 | import android.support.v7.app.ActionBarActivity; 21 | import android.support.v7.widget.Toolbar; 22 | import android.view.MenuItem; 23 | 24 | import com.xargsgrep.portknocker.R; 25 | import com.xargsgrep.portknocker.fragment.SettingsFragment; 26 | 27 | public class SettingsActivity extends ActionBarActivity 28 | { 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) 31 | { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.settings); 34 | 35 | getFragmentManager() 36 | .beginTransaction() 37 | .replace(R.id.fragment_content, new SettingsFragment()) 38 | .commit(); 39 | 40 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 41 | setSupportActionBar(toolbar); 42 | 43 | getSupportActionBar().setSubtitle(getResources().getString(R.string.settings_subtitle)); 44 | getSupportActionBar().setHomeButtonEnabled(true); 45 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 46 | } 47 | 48 | @Override 49 | public boolean onOptionsItemSelected(MenuItem item) 50 | { 51 | switch (item.getItemId()) 52 | { 53 | case android.R.id.home: 54 | Intent hostListIntent = new Intent(this, HostListActivity.class); 55 | hostListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 56 | startActivity(hostListIntent); 57 | return true; 58 | default: 59 | return super.onOptionsItemSelected(item); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/activity/SettingsActivityCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.activity; 17 | 18 | import android.content.Intent; 19 | import android.content.SharedPreferences; 20 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 21 | import android.os.Bundle; 22 | import android.preference.PreferenceActivity; 23 | import android.view.MenuItem; 24 | 25 | import com.xargsgrep.portknocker.R; 26 | import com.xargsgrep.portknocker.widget.HostWidget; 27 | 28 | public class SettingsActivityCompat extends PreferenceActivity implements OnSharedPreferenceChangeListener 29 | { 30 | @Override 31 | @SuppressWarnings("deprecation") 32 | public void onCreate(Bundle savedInstanceState) 33 | { 34 | super.onCreate(savedInstanceState); 35 | 36 | addPreferencesFromResource(R.xml.preferences); 37 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 38 | } 39 | 40 | @Override 41 | public boolean onOptionsItemSelected(MenuItem item) 42 | { 43 | switch (item.getItemId()) 44 | { 45 | case android.R.id.home: 46 | Intent hostListIntent = new Intent(this, HostListActivity.class); 47 | hostListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 48 | startActivity(hostListIntent); 49 | return true; 50 | default: 51 | return super.onOptionsItemSelected(item); 52 | } 53 | } 54 | 55 | @Override 56 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) 57 | { 58 | if (getString(R.string.pref_key_hide_ports_widget).equals(key)) 59 | { 60 | HostWidget.updateAllAppWidgets(this); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/utils/SerializationUtils.java: -------------------------------------------------------------------------------- 1 | package com.xargsgrep.portknocker.utils; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.net.Uri; 6 | import android.os.Environment; 7 | import android.support.annotation.NonNull; 8 | 9 | import com.fasterxml.jackson.core.type.TypeReference; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | import com.fasterxml.jackson.databind.SerializationFeature; 12 | import com.xargsgrep.portknocker.model.Host; 13 | 14 | import java.io.File; 15 | import java.io.FileWriter; 16 | import java.io.InputStream; 17 | import java.util.List; 18 | 19 | public class SerializationUtils 20 | { 21 | public static String serializeHosts(String fileName, List hosts) throws Exception 22 | { 23 | if (!isExternalStorageAccessible()) 24 | { 25 | throw new Exception("External storage is not currently accessible"); 26 | } 27 | 28 | ObjectMapper mapper = new ObjectMapper(); 29 | mapper.enable(SerializationFeature.INDENT_OUTPUT); 30 | 31 | File storageDir = createPortKnockerFolderIfNotExists(); 32 | 33 | File file = new File(storageDir, fileName); 34 | FileWriter fileWriter = new FileWriter(file); 35 | 36 | mapper.writeValue(fileWriter, hosts); 37 | 38 | return file.getAbsolutePath(); 39 | } 40 | 41 | @NonNull 42 | public static File createPortKnockerFolderIfNotExists() { 43 | File storageDir = new File(Environment.getExternalStorageDirectory(), "PortKnocker"); 44 | if (!storageDir.exists()) 45 | { 46 | storageDir.mkdirs(); 47 | } 48 | return storageDir; 49 | } 50 | 51 | public static List deserializeHosts(Context context, Uri fileUri) throws Exception 52 | { 53 | if (!isExternalStorageAccessible()) 54 | { 55 | throw new Exception("External storage is not currently accessible"); 56 | } 57 | 58 | ObjectMapper objectMapper = new ObjectMapper(); 59 | ContentResolver resolver = context.getContentResolver(); 60 | InputStream input = resolver.openInputStream(fileUri); 61 | 62 | return objectMapper.readValue(input, new TypeReference>() {}); 63 | } 64 | 65 | private static boolean isExternalStorageAccessible() 66 | { 67 | String state = Environment.getExternalStorageState(); 68 | return Environment.MEDIA_MOUNTED.equals(state); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/adapter/EditHostFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.adapter; 17 | 18 | import android.content.Context; 19 | import android.support.v4.app.Fragment; 20 | import android.support.v4.app.FragmentManager; 21 | import android.support.v4.app.FragmentPagerAdapter; 22 | 23 | import com.xargsgrep.portknocker.R; 24 | import com.xargsgrep.portknocker.activity.EditHostActivity; 25 | import com.xargsgrep.portknocker.fragment.HostFragment; 26 | import com.xargsgrep.portknocker.fragment.MiscFragment; 27 | import com.xargsgrep.portknocker.fragment.PortsFragment; 28 | 29 | public class EditHostFragmentPagerAdapter extends FragmentPagerAdapter 30 | { 31 | private Context context; 32 | private Long hostId; 33 | 34 | public EditHostFragmentPagerAdapter(FragmentManager fm, Context context, Long hostId) 35 | { 36 | super(fm); 37 | this.context = context; 38 | this.hostId = hostId; 39 | } 40 | 41 | @Override 42 | public Fragment getItem(int position) 43 | { 44 | switch (position) 45 | { 46 | case EditHostActivity.TAB_INDEX_HOST: 47 | return HostFragment.newInstance(hostId); 48 | case EditHostActivity.TAB_INDEX_PORTS: 49 | return PortsFragment.newInstance(hostId); 50 | case EditHostActivity.TAB_INDEX_MISC: 51 | return MiscFragment.newInstance(hostId); 52 | } 53 | throw new IllegalArgumentException("Invalid item position in ViewPager: " + position); 54 | } 55 | 56 | @Override 57 | public CharSequence getPageTitle(int position) 58 | { 59 | switch (position) 60 | { 61 | case EditHostActivity.TAB_INDEX_HOST: 62 | return context.getString(R.string.host_tab_name); 63 | case EditHostActivity.TAB_INDEX_PORTS: 64 | return context.getString(R.string.ports_tab_name); 65 | case EditHostActivity.TAB_INDEX_MISC: 66 | return context.getString(R.string.misc_tab_name); 67 | } 68 | throw new IllegalArgumentException("Invalid item position in ViewPager: " + position); 69 | } 70 | 71 | @Override 72 | public int getCount() 73 | { 74 | return 3; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/adapter/ApplicationArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.adapter; 17 | 18 | import android.content.Context; 19 | import android.view.LayoutInflater; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.widget.ArrayAdapter; 23 | import android.widget.ImageView; 24 | import android.widget.TextView; 25 | 26 | import com.xargsgrep.portknocker.R; 27 | import com.xargsgrep.portknocker.model.Application; 28 | 29 | import java.util.List; 30 | 31 | public class ApplicationArrayAdapter extends ArrayAdapter 32 | { 33 | private Context context; 34 | private List applications; 35 | 36 | public ApplicationArrayAdapter(Context context, List applications) 37 | { 38 | super(context, -1, applications); 39 | this.context = context; 40 | this.applications = applications; 41 | } 42 | 43 | @Override 44 | public int getCount() 45 | { 46 | return applications.size(); 47 | } 48 | 49 | @Override 50 | public Application getItem(int position) 51 | { 52 | return applications.get(position); 53 | } 54 | 55 | @Override 56 | public View getView(int position, View convertView, ViewGroup parent) 57 | { 58 | return getView(position, convertView); 59 | } 60 | 61 | @Override 62 | public View getDropDownView(int position, View convertView, ViewGroup parent) 63 | { 64 | return getView(position, convertView); 65 | } 66 | 67 | private View getView(int position, View convertView) 68 | { 69 | View view = convertView; 70 | if (view == null) 71 | { 72 | view = LayoutInflater.from(getContext()).inflate(R.layout.icon_text_item, null); 73 | } 74 | 75 | ImageView imageView = (ImageView) view.findViewById(R.id.icon); 76 | TextView textView = (TextView) view.findViewById(R.id.text); 77 | 78 | Application application = applications.get(position); 79 | // imageView.setImageDrawable(application.getIcon() == null ? context.getResources().getDrawable(R.drawable.ic_launcher) : application.getIcon()); 80 | imageView.setImageDrawable(application.getIcon() == null ? null : application.getIcon()); 81 | textView.setText(application.getLabel()); 82 | 83 | return view; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/res/layout/host_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 25 | 26 | 33 | 34 | 41 | 42 | 50 | 51 | 52 | 59 | 60 | 72 | 73 | 80 | 81 | 93 | 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/model/Host.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.model; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public class Host 24 | { 25 | public static final int DEFAULT_DELAY = 1000; 26 | public static final int DEFAULT_TCP_CONNECT_TIMEOUT = 100; 27 | 28 | private long id; 29 | private String label; 30 | private String hostname; 31 | private int delay = DEFAULT_DELAY; 32 | private int tcpConnectTimeout = DEFAULT_TCP_CONNECT_TIMEOUT; 33 | private String launchIntentPackage; 34 | private List ports = new ArrayList<>(); 35 | 36 | @JsonIgnore 37 | public long getId() 38 | { 39 | return id; 40 | } 41 | 42 | public void setId(long id) 43 | { 44 | this.id = id; 45 | } 46 | 47 | public String getLabel() 48 | { 49 | return label; 50 | } 51 | 52 | public void setLabel(String label) 53 | { 54 | this.label = label; 55 | } 56 | 57 | public String getHostname() 58 | { 59 | return hostname; 60 | } 61 | 62 | public void setHostname(String hostname) 63 | { 64 | this.hostname = hostname; 65 | } 66 | 67 | public int getDelay() 68 | { 69 | return delay; 70 | } 71 | 72 | public void setDelay(int delay) 73 | { 74 | this.delay = delay; 75 | } 76 | 77 | public String getLaunchIntentPackage() 78 | { 79 | return launchIntentPackage; 80 | } 81 | 82 | public int getTcpConnectTimeout() 83 | { 84 | return tcpConnectTimeout; 85 | } 86 | 87 | public void setTcpConnectTimeout(int tcpConnectTimeout) 88 | { 89 | this.tcpConnectTimeout = tcpConnectTimeout; 90 | } 91 | 92 | public void setLaunchIntentPackage(String launchIntentPackage) 93 | { 94 | this.launchIntentPackage = launchIntentPackage; 95 | } 96 | 97 | public List getPorts() 98 | { 99 | return ports; 100 | } 101 | 102 | public void setPorts(List ports) 103 | { 104 | this.ports = ports; 105 | } 106 | 107 | @JsonIgnore 108 | public String getPortsString() 109 | { 110 | StringBuilder portsString = new StringBuilder(); 111 | 112 | if (ports.size() > 0) 113 | { 114 | for (Port port : ports) 115 | { 116 | portsString.append(port.getPort()); 117 | portsString.append(":"); 118 | portsString.append(port.getProtocol()); 119 | portsString.append(", "); 120 | } 121 | portsString.replace(portsString.length() - 2, portsString.length(), ""); 122 | } 123 | 124 | return portsString.toString(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /FileChooser/src/main/java/com/ipaulpro/afilechooser/FileListAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.ipaulpro.afilechooser; 18 | 19 | import android.content.Context; 20 | import android.view.LayoutInflater; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | import android.widget.BaseAdapter; 24 | import android.widget.TextView; 25 | 26 | import java.io.File; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | /** 31 | * List adapter for Files. 32 | * 33 | * @version 2013-12-11 34 | * @author paulburke (ipaulpro) 35 | */ 36 | public class FileListAdapter extends BaseAdapter { 37 | 38 | private final static int ICON_FOLDER = R.drawable.ic_folder; 39 | private final static int ICON_FILE = R.drawable.ic_file; 40 | 41 | private final LayoutInflater mInflater; 42 | 43 | private List mData = new ArrayList(); 44 | 45 | public FileListAdapter(Context context) { 46 | mInflater = LayoutInflater.from(context); 47 | } 48 | 49 | public void add(File file) { 50 | mData.add(file); 51 | notifyDataSetChanged(); 52 | } 53 | 54 | public void remove(File file) { 55 | mData.remove(file); 56 | notifyDataSetChanged(); 57 | } 58 | 59 | public void insert(File file, int index) { 60 | mData.add(index, file); 61 | notifyDataSetChanged(); 62 | } 63 | 64 | public void clear() { 65 | mData.clear(); 66 | notifyDataSetChanged(); 67 | } 68 | 69 | @Override 70 | public File getItem(int position) { 71 | return mData.get(position); 72 | } 73 | 74 | @Override 75 | public long getItemId(int position) { 76 | return position; 77 | } 78 | 79 | @Override 80 | public int getCount() { 81 | return mData.size(); 82 | } 83 | 84 | public List getListItems() { 85 | return mData; 86 | } 87 | 88 | /** 89 | * Set the list items without notifying on the clear. This prevents loss of 90 | * scroll position. 91 | * 92 | * @param data 93 | */ 94 | public void setListItems(List data) { 95 | mData = data; 96 | notifyDataSetChanged(); 97 | } 98 | 99 | @Override 100 | public View getView(int position, View convertView, ViewGroup parent) { 101 | View row = convertView; 102 | 103 | if (row == null) 104 | row = mInflater.inflate(R.layout.file, parent, false); 105 | 106 | TextView view = (TextView) row; 107 | 108 | // Get the file at the current position 109 | final File file = getItem(position); 110 | 111 | // Set the TextView as the file name 112 | view.setText(file.getName()); 113 | 114 | // If the item is not a directory, use the file icon 115 | int icon = file.isDirectory() ? ICON_FOLDER : ICON_FILE; 116 | view.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0); 117 | 118 | return row; 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/misc_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 29 | 35 | 36 | 45 | 53 | 60 | 68 | 69 | 70 | 79 | 87 | 94 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/ConfigureWidgetActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.widget; 17 | 18 | import android.app.ListActivity; 19 | import android.appwidget.AppWidgetManager; 20 | import android.content.Context; 21 | import android.content.SharedPreferences; 22 | import android.os.Bundle; 23 | 24 | import com.xargsgrep.portknocker.R; 25 | import com.xargsgrep.portknocker.db.DatabaseManager; 26 | import com.xargsgrep.portknocker.model.Host; 27 | 28 | import java.util.List; 29 | 30 | public class ConfigureWidgetActivity extends ListActivity 31 | { 32 | private static final String PREFS_NAME = "com.xargsgrep.portknocker.widget"; 33 | private static final String PREF_HOST_ID_KEY = "hostid_"; 34 | private static final String PREF_CONFIGURED_KEY = "configured_"; 35 | 36 | private DatabaseManager databaseManager; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) 40 | { 41 | super.onCreate(savedInstanceState); 42 | setResult(RESULT_CANCELED); 43 | setContentView(R.layout.list_view); 44 | 45 | Bundle extras = getIntent().getExtras(); 46 | int appWidgetId = (extras == null) 47 | ? AppWidgetManager.INVALID_APPWIDGET_ID 48 | : extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); 49 | if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) finish(); 50 | 51 | databaseManager = new DatabaseManager(this); 52 | List hosts = databaseManager.getAllHosts(); 53 | ConfigureWidgetHostArrayAdapter hostAdapter = new ConfigureWidgetHostArrayAdapter(this, hosts, appWidgetId); 54 | setListAdapter(hostAdapter); 55 | } 56 | 57 | public static void saveHostIdPreference(Context context, int appWidgetId, long hostId) 58 | { 59 | SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); 60 | prefs.putLong(getAppWidgetHostIdKey(appWidgetId), hostId); 61 | prefs.commit(); 62 | } 63 | 64 | public static long getHostIdPreference(Context context, int appWidgetId) 65 | { 66 | SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); 67 | return prefs.getLong(getAppWidgetHostIdKey(appWidgetId), -1); 68 | } 69 | 70 | public static void deleteHostIdPreference(Context context, int appWidgetId) 71 | { 72 | SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); 73 | prefs.remove(getAppWidgetHostIdKey(appWidgetId)); 74 | prefs.commit(); 75 | } 76 | 77 | public static void saveConfiguredPreference(Context context, int appWidgetId) 78 | { 79 | SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); 80 | prefs.putBoolean(getAppWidgetConfiguredKey(appWidgetId), true); 81 | prefs.commit(); 82 | } 83 | 84 | public static boolean getConfiguredPreference(Context context, int appWidgetId) 85 | { 86 | SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); 87 | return prefs.getBoolean(getAppWidgetConfiguredKey(appWidgetId), false); 88 | } 89 | 90 | private static String getAppWidgetHostIdKey(int appWidgetId) 91 | { 92 | return PREF_HOST_ID_KEY + appWidgetId; 93 | } 94 | 95 | private static String getAppWidgetConfiguredKey(int appWidgetId) 96 | { 97 | return PREF_CONFIGURED_KEY + appWidgetId; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /FileChooser/src/main/res/xml/mimetypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Port Knocker 4 | Port Knocker 1x1 5 | Port Knocker 2x1 6 | Port Knocker 3x1 7 | Port Knocker 4x1 8 | ホスト 9 | ポート 10 | その他 11 | ラベル 12 | ラベル 13 | ホスト名/IP 14 | domain.com/192.168.10.1 15 | ポート 16 | 1234 17 | プロトコル 18 | パケット間の待機 (ms) 19 | 1000 20 | アプリケーションの起動 21 | なし 22 | TCP 接続タイムアウト (ms) 23 | はい 24 | いいえ 25 | ホストを削除しますか? 26 | 変更を破棄しますか? 27 | アプリケーションの取得中… 28 | パケットの送信中… 29 | hide_ports 30 | ホストリストでポートを非表示 31 | ホストリストにポートシーケンスを表示しません 32 | hide_ports_widget 33 | ウィジェットでポートを非表示 34 | ウィジェットにポートシーケンスを表示しません 35 | その他 36 | 設定 37 | アイテムはありません 38 | "すべてのポートを削除できません" 39 | ラベルを入力してください 40 | ホスト名を入力してください 41 | ホスト名/IPが正しくありません 42 | ポートを 1 つ以上入力してください 43 | "これ以上待機できません " 44 | "無効なポート: " 45 | 保存しました 46 | 保存に失敗しました 47 | ノックが完了しました 48 | ノックをキャンセルしました 49 | "ノックに失敗しました: " 50 | "ファイルからホストをインポートしました: " 51 | "ホストのインポートに失敗しました: " 52 | 外部メモリへのアクセス許可がありません。 53 | 設定 54 | ホストのエクスポート 55 | ホストのインポート 56 | ホストを送信 57 | ホストを並び替え 58 | ホスト名順 59 | ラベル順 60 | 最新順 61 | ホストのエクスポート 62 | ファイル名を入力してください 63 | OK 64 | キャンセル 65 | "ホストをファイルにエクスポートしました: " 66 | "ホストのエクスポートに失敗しました: " 67 | "共有... " 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/fragment/ProgressDialogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.fragment; 17 | 18 | import android.app.Dialog; 19 | import android.app.ProgressDialog; 20 | import android.content.DialogInterface; 21 | import android.os.AsyncTask; 22 | import android.os.Bundle; 23 | import android.support.v4.app.DialogFragment; 24 | 25 | public class ProgressDialogFragment extends DialogFragment 26 | { 27 | public static final String TAG = "ProgressDialogFragment"; 28 | 29 | private static final String KEY_MESSAGE = "message"; 30 | private static final String KEY_INDETERMINATE = "indeterminate"; 31 | private static final String KEY_STYLE = "style"; 32 | private static final String KEY_MAX = "max"; 33 | 34 | private ProgressDialog mDialog; 35 | private AsyncTask mAsyncTask; 36 | private int mDialogProgress = 0; 37 | 38 | public static ProgressDialogFragment newInstance(AsyncTask asyncTask, String message, boolean indeterminate, int style, int max) 39 | { 40 | ProgressDialogFragment fragment = new ProgressDialogFragment(); 41 | fragment.mAsyncTask = asyncTask; 42 | 43 | Bundle args = new Bundle(); 44 | args.putString(KEY_MESSAGE, message); 45 | args.putBoolean(KEY_INDETERMINATE, indeterminate); 46 | args.putInt(KEY_STYLE, style); 47 | args.putInt(KEY_MAX, max); 48 | fragment.setArguments(args); 49 | 50 | return fragment; 51 | } 52 | 53 | public static ProgressDialogFragment newInstance(String message, boolean indeterminate, int style) 54 | { 55 | return newInstance(null, message, indeterminate, style, 100); 56 | } 57 | 58 | @Override 59 | public Dialog getDialog() 60 | { 61 | return mDialog; 62 | } 63 | 64 | @Override 65 | public void onCreate(Bundle savedInstanceState) 66 | { 67 | super.onCreate(savedInstanceState); 68 | setRetainInstance(true); 69 | } 70 | 71 | @Override 72 | public Dialog onCreateDialog(Bundle savedInstanceState) 73 | { 74 | mDialog = new ProgressDialog(getActivity()); 75 | mDialog.setMessage(getArguments().getString(KEY_MESSAGE)); 76 | mDialog.setIndeterminate(getArguments().getBoolean(KEY_INDETERMINATE)); 77 | mDialog.setProgressStyle(getArguments().getInt(KEY_STYLE)); 78 | mDialog.setMax(getArguments().getInt(KEY_MAX)); 79 | return mDialog; 80 | } 81 | 82 | @Override 83 | public void onStart() 84 | { 85 | super.onStart(); 86 | mDialog.setProgress(mDialogProgress); 87 | } 88 | 89 | @Override 90 | public void onSaveInstanceState(Bundle outState) 91 | { 92 | super.onSaveInstanceState(outState); 93 | mDialogProgress = mDialog.getProgress(); 94 | } 95 | 96 | @Override 97 | public void onCancel(DialogInterface dialog) 98 | { 99 | super.onCancel(dialog); 100 | if (isCancelable() && mAsyncTask != null) mAsyncTask.cancel(true); 101 | } 102 | 103 | @Override 104 | public void onDestroyView() 105 | { 106 | // NOTE: without this the dialog doesn't seem to get recreated on screen rotation 107 | if (mDialog != null && getRetainInstance()) 108 | { 109 | mDialog.setOnDismissListener(null); 110 | } 111 | super.onDestroyView(); 112 | } 113 | 114 | @Override 115 | public void onDestroy() 116 | { 117 | super.onDestroy(); 118 | mDialog = null; 119 | mAsyncTask = null; 120 | } 121 | 122 | public void setProgress(int progress) 123 | { 124 | if (mDialog != null) mDialog.setProgress(progress); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /FileChooser/src/main/java/com/ipaulpro/afilechooser/FileLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.ipaulpro.afilechooser; 18 | 19 | import android.content.Context; 20 | import android.os.FileObserver; 21 | import android.support.v4.content.AsyncTaskLoader; 22 | 23 | import com.ipaulpro.afilechooser.utils.FileUtils; 24 | 25 | import java.io.File; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | /** 31 | * Loader that returns a list of Files in a given file path. 32 | * 33 | * @version 2013-12-11 34 | * @author paulburke (ipaulpro) 35 | */ 36 | public class FileLoader extends AsyncTaskLoader> { 37 | 38 | private static final int FILE_OBSERVER_MASK = FileObserver.CREATE 39 | | FileObserver.DELETE | FileObserver.DELETE_SELF 40 | | FileObserver.MOVED_FROM | FileObserver.MOVED_TO 41 | | FileObserver.MODIFY | FileObserver.MOVE_SELF; 42 | 43 | private FileObserver mFileObserver; 44 | 45 | private List mData; 46 | private String mPath; 47 | 48 | public FileLoader(Context context, String path) { 49 | super(context); 50 | this.mPath = path; 51 | } 52 | 53 | @Override 54 | public List loadInBackground() { 55 | 56 | ArrayList list = new ArrayList(); 57 | 58 | // Current directory File instance 59 | final File pathDir = new File(mPath); 60 | 61 | // List file in this directory with the directory filter 62 | final File[] dirs = pathDir.listFiles(FileUtils.sDirFilter); 63 | if (dirs != null) { 64 | // Sort the folders alphabetically 65 | Arrays.sort(dirs, FileUtils.sComparator); 66 | // Add each folder to the File list for the list adapter 67 | for (File dir : dirs) 68 | list.add(dir); 69 | } 70 | 71 | // List file in this directory with the file filter 72 | final File[] files = pathDir.listFiles(FileUtils.sFileFilter); 73 | if (files != null) { 74 | // Sort the files alphabetically 75 | Arrays.sort(files, FileUtils.sComparator); 76 | // Add each file to the File list for the list adapter 77 | for (File file : files) 78 | list.add(file); 79 | } 80 | 81 | return list; 82 | } 83 | 84 | @Override 85 | public void deliverResult(List data) { 86 | if (isReset()) { 87 | onReleaseResources(data); 88 | return; 89 | } 90 | 91 | List oldData = mData; 92 | mData = data; 93 | 94 | if (isStarted()) 95 | super.deliverResult(data); 96 | 97 | if (oldData != null && oldData != data) 98 | onReleaseResources(oldData); 99 | } 100 | 101 | @Override 102 | protected void onStartLoading() { 103 | if (mData != null) 104 | deliverResult(mData); 105 | 106 | if (mFileObserver == null) { 107 | mFileObserver = new FileObserver(mPath, FILE_OBSERVER_MASK) { 108 | @Override 109 | public void onEvent(int event, String path) { 110 | onContentChanged(); 111 | } 112 | }; 113 | } 114 | mFileObserver.startWatching(); 115 | 116 | if (takeContentChanged() || mData == null) 117 | forceLoad(); 118 | } 119 | 120 | @Override 121 | protected void onStopLoading() { 122 | cancelLoad(); 123 | } 124 | 125 | @Override 126 | protected void onReset() { 127 | onStopLoading(); 128 | 129 | if (mData != null) { 130 | onReleaseResources(mData); 131 | mData = null; 132 | } 133 | } 134 | 135 | @Override 136 | public void onCanceled(List data) { 137 | super.onCanceled(data); 138 | 139 | onReleaseResources(data); 140 | } 141 | 142 | protected void onReleaseResources(List data) { 143 | 144 | if (mFileObserver != null) { 145 | mFileObserver.stopWatching(); 146 | mFileObserver = null; 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/adapter/HostArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.adapter; 17 | 18 | import android.content.Intent; 19 | import android.content.SharedPreferences; 20 | import android.preference.PreferenceManager; 21 | import android.support.v4.app.Fragment; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.View.OnClickListener; 25 | import android.view.ViewGroup; 26 | import android.widget.ArrayAdapter; 27 | import android.widget.ImageButton; 28 | import android.widget.TextView; 29 | 30 | import com.xargsgrep.portknocker.R; 31 | import com.xargsgrep.portknocker.activity.EditHostActivity; 32 | import com.xargsgrep.portknocker.asynctask.KnockerAsyncTask; 33 | import com.xargsgrep.portknocker.fragment.HostListFragment; 34 | import com.xargsgrep.portknocker.model.Host; 35 | 36 | import java.util.List; 37 | 38 | public class HostArrayAdapter extends ArrayAdapter 39 | { 40 | private Fragment fragment; 41 | 42 | public HostArrayAdapter(Fragment fragment, List hosts) 43 | { 44 | super(fragment.getActivity(), -1, hosts); 45 | this.fragment = fragment; 46 | } 47 | 48 | @Override 49 | public View getView(int position, View convertView, ViewGroup parent) 50 | { 51 | View view = convertView; 52 | if (view == null) view = LayoutInflater.from(getContext()).inflate(R.layout.host_row, null); 53 | 54 | TextView labelView = (TextView) view.findViewById(R.id.host_row_label); 55 | TextView hostnameView = (TextView) view.findViewById(R.id.host_row_hostname); 56 | TextView portsView = (TextView) view.findViewById(R.id.host_row_ports); 57 | 58 | Host host = getItem(position); 59 | 60 | labelView.setText(host.getLabel()); 61 | hostnameView.setText(host.getHostname()); 62 | portsView.setText(host.getPortsString()); 63 | 64 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(fragment.getActivity()); 65 | if (sharedPreferences.getBoolean(fragment.getActivity().getString(R.string.pref_key_hide_ports), false)) 66 | { 67 | portsView.setVisibility(View.GONE); 68 | } 69 | 70 | final int fPosition = position; 71 | view.setOnClickListener(new OnClickListener() 72 | { 73 | @Override 74 | public void onClick(View v) 75 | { 76 | Host host = getItem(fPosition); 77 | KnockerAsyncTask knockerAsyncTask = new KnockerAsyncTask(fragment.getActivity(), host.getPorts().size()); 78 | knockerAsyncTask.execute(host); 79 | } 80 | }); 81 | 82 | ImageButton deleteButton = (ImageButton) view.findViewById(R.id.host_row_delete); 83 | deleteButton.setOnClickListener(new OnClickListener() 84 | { 85 | @Override 86 | public void onClick(View v) 87 | { 88 | ((HostListFragment) fragment).setDeleteHostPosition(fPosition); 89 | ((HostListFragment) fragment).showDeleteDialog(); 90 | } 91 | }); 92 | 93 | ImageButton editButton = (ImageButton) view.findViewById(R.id.host_row_edit); 94 | editButton.setOnClickListener(new OnClickListener() 95 | { 96 | @Override 97 | public void onClick(View v) 98 | { 99 | Host host = getItem(fPosition); 100 | Intent editHostIntent = new Intent(fragment.getActivity(), EditHostActivity.class); 101 | editHostIntent.putExtra(EditHostActivity.KEY_HOST_ID, host.getId()); 102 | fragment.getActivity().startActivity(editHostIntent); 103 | } 104 | }); 105 | 106 | return view; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/asynctask/RetrieveApplicationsAsyncTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.asynctask; 17 | 18 | import android.content.pm.ApplicationInfo; 19 | import android.content.pm.PackageManager; 20 | import android.os.AsyncTask; 21 | import android.support.v4.app.Fragment; 22 | import android.support.v4.app.FragmentActivity; 23 | import android.support.v4.app.FragmentManager; 24 | import android.support.v4.app.FragmentTransaction; 25 | 26 | import com.xargsgrep.portknocker.R; 27 | import com.xargsgrep.portknocker.fragment.MiscFragment; 28 | import com.xargsgrep.portknocker.fragment.ProgressDialogFragment; 29 | import com.xargsgrep.portknocker.model.Application; 30 | 31 | import java.util.ArrayList; 32 | import java.util.Collections; 33 | import java.util.Comparator; 34 | import java.util.List; 35 | 36 | public class RetrieveApplicationsAsyncTask extends AsyncTask> 37 | { 38 | private FragmentActivity activity; 39 | private Fragment fragment; 40 | 41 | public RetrieveApplicationsAsyncTask(FragmentActivity activity, Fragment fragment) 42 | { 43 | this.activity = activity; 44 | this.fragment = fragment; 45 | } 46 | 47 | @Override 48 | protected void onPreExecute() 49 | { 50 | FragmentManager fragmentManager = activity.getSupportFragmentManager(); 51 | 52 | FragmentTransaction ft = fragmentManager.beginTransaction(); 53 | Fragment prev = fragmentManager.findFragmentByTag(ProgressDialogFragment.TAG); 54 | if (prev != null) ft.remove(prev); 55 | ft.addToBackStack(null); 56 | ft.commit(); 57 | 58 | // ProgressDialogFragment dialogFragment = ProgressDialogFragment.newInstance(activity.getString(R.string.progress_dialog_retrieving_applications), true, ProgressDialog.STYLE_SPINNER); 59 | // dialogFragment.setCancelable(false); 60 | // dialogFragment.show(ft, ProgressDialogFragment.TAG); 61 | } 62 | 63 | @Override 64 | protected List doInBackground(Void... params) 65 | { 66 | PackageManager packageManager = activity.getPackageManager(); 67 | List installedApplications = packageManager.getInstalledApplications(PackageManager.GET_META_DATA); 68 | 69 | List applications = new ArrayList<>(); 70 | for (ApplicationInfo applicationInfo : installedApplications) 71 | { 72 | if (isSystemPackage(applicationInfo) || packageManager.getLaunchIntentForPackage(applicationInfo.packageName) == null) 73 | continue; 74 | applications.add(new Application(packageManager.getApplicationLabel(applicationInfo).toString(), applicationInfo.loadIcon(packageManager), applicationInfo.packageName)); 75 | } 76 | 77 | Collections.sort(applications, new Comparator() 78 | { 79 | @Override 80 | public int compare(Application app1, Application app2) 81 | { 82 | return app1.getLabel().compareTo(app2.getLabel()); 83 | } 84 | }); 85 | applications.add(0, new Application(activity.getString(R.string.launch_app_list_default), null, "")); 86 | 87 | return applications; 88 | } 89 | 90 | @Override 91 | protected void onPostExecute(List applications) 92 | { 93 | // FragmentManager fragmentManager = activity.getSupportFragmentManager(); 94 | 95 | ((MiscFragment) fragment).initializeApplicationAdapter(applications); 96 | // Fragment dialog = fragmentManager.findFragmentByTag(ProgressDialogFragment.TAG); 97 | // if (dialog != null) ((ProgressDialogFragment) dialog).dismiss(); 98 | } 99 | 100 | private boolean isSystemPackage(ApplicationInfo applicationInfo) 101 | { 102 | return ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Port Knocker 4 | Port Knocker 1x1 5 | Port Knocker 2x1 6 | Port Knocker 3x1 7 | Port Knocker 4x1 8 | Host 9 | Ports 10 | Other 11 | Label 12 | Label 13 | Hostname/IP 14 | domain.com/192.168.10.1 15 | Port 16 | 1234 17 | Protocol 18 | Delay Between Packets (ms) 19 | 1000 20 | Launch Application 21 | None 22 | TCP Connect Timeout (ms) 23 | Yes 24 | No 25 | Delete host? 26 | Discard changes? 27 | Retrieving applications… 28 | Sending packets… 29 | hide_ports 30 | Hide ports in host list 31 | Port sequence will not be displayed in the host list 32 | hide_ports_widget 33 | Hide ports in widget 34 | Port sequence will not be displayed in widgets 35 | Miscellaneous 36 | Settings 37 | There are no items 38 | "Can't delete all ports" 39 | Please enter a label 40 | Please enter a hostname 41 | Invalid hostname/IP 42 | Please enter at least one port 43 | "Delay can't be more than " 44 | "Invalid port: " 45 | Saved 46 | Save failed 47 | Knocking complete 48 | Knocking canceled 49 | "Knocking failed: " 50 | "Imported hosts from file: " 51 | "Importing hosts failed: " 52 | No permission to access external memory. 53 | Settings 54 | Export Hosts 55 | Import Hosts 56 | Send Hosts 57 | Sort Hosts 58 | By hostname 59 | By label 60 | By newest 61 | Export Hosts 62 | Enter filename 63 | OK 64 | Cancel 65 | "Exported hosts to file: " 66 | "Exporting hosts failed: " 67 | "Share with... " 68 | -------------------------------------------------------------------------------- /FileChooser/src/main/java/com/ipaulpro/afilechooser/FileListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.ipaulpro.afilechooser; 18 | 19 | import android.app.Activity; 20 | import android.os.Bundle; 21 | import android.os.Environment; 22 | import android.support.v4.app.ListFragment; 23 | import android.support.v4.app.LoaderManager; 24 | import android.support.v4.content.Loader; 25 | import android.view.View; 26 | import android.widget.ListView; 27 | 28 | import java.io.File; 29 | import java.util.List; 30 | 31 | /** 32 | * Fragment that displays a list of Files in a given path. 33 | * 34 | * @version 2013-12-11 35 | * @author paulburke (ipaulpro) 36 | */ 37 | public class FileListFragment extends ListFragment implements 38 | LoaderManager.LoaderCallbacks> { 39 | 40 | /** 41 | * Interface to listen for events. 42 | */ 43 | public interface Callbacks { 44 | /** 45 | * Called when a file is selected from the list. 46 | * 47 | * @param file The file selected 48 | */ 49 | public void onFileSelected(File file); 50 | } 51 | 52 | private static final int LOADER_ID = 0; 53 | 54 | private FileListAdapter mAdapter; 55 | private String mPath; 56 | 57 | private Callbacks mListener; 58 | 59 | /** 60 | * Create a new instance with the given file path. 61 | * 62 | * @param path The absolute path of the file (directory) to display. 63 | * @return A new Fragment with the given file path. 64 | */ 65 | public static FileListFragment newInstance(String path) { 66 | FileListFragment fragment = new FileListFragment(); 67 | Bundle args = new Bundle(); 68 | args.putString(FileChooserActivity.PATH, path); 69 | fragment.setArguments(args); 70 | 71 | return fragment; 72 | } 73 | 74 | @Override 75 | public void onAttach(Activity activity) { 76 | super.onAttach(activity); 77 | 78 | try { 79 | mListener = (Callbacks) activity; 80 | } catch (ClassCastException e) { 81 | throw new ClassCastException(activity.toString() 82 | + " must implement FileListFragment.Callbacks"); 83 | } 84 | } 85 | 86 | @Override 87 | public void onCreate(Bundle savedInstanceState) { 88 | super.onCreate(savedInstanceState); 89 | 90 | mAdapter = new FileListAdapter(getActivity()); 91 | mPath = getArguments() != null ? getArguments().getString( 92 | FileChooserActivity.PATH) : Environment 93 | .getExternalStorageDirectory().getAbsolutePath(); 94 | } 95 | 96 | @Override 97 | public void onActivityCreated(Bundle savedInstanceState) { 98 | setEmptyText(getString(R.string.empty_directory)); 99 | setListAdapter(mAdapter); 100 | setListShown(false); 101 | 102 | getLoaderManager().initLoader(LOADER_ID, null, this); 103 | 104 | super.onActivityCreated(savedInstanceState); 105 | } 106 | 107 | @Override 108 | public void onListItemClick(ListView l, View v, int position, long id) { 109 | FileListAdapter adapter = (FileListAdapter) l.getAdapter(); 110 | if (adapter != null) { 111 | File file = (File) adapter.getItem(position); 112 | mPath = file.getAbsolutePath(); 113 | mListener.onFileSelected(file); 114 | } 115 | } 116 | 117 | @Override 118 | public Loader> onCreateLoader(int id, Bundle args) { 119 | return new FileLoader(getActivity(), mPath); 120 | } 121 | 122 | @Override 123 | public void onLoadFinished(Loader> loader, List data) { 124 | mAdapter.setListItems(data); 125 | 126 | if (isResumed()) 127 | setListShown(true); 128 | else 129 | setListShownNoAnimation(true); 130 | } 131 | 132 | @Override 133 | public void onLoaderReset(Loader> loader) { 134 | mAdapter.clear(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/ConfigureWidgetHostArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.widget; 17 | 18 | import android.app.Activity; 19 | import android.appwidget.AppWidgetManager; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.SharedPreferences; 23 | import android.preference.PreferenceManager; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.View.OnClickListener; 27 | import android.view.ViewGroup; 28 | import android.widget.ArrayAdapter; 29 | import android.widget.TextView; 30 | 31 | import com.xargsgrep.portknocker.R; 32 | import com.xargsgrep.portknocker.db.DatabaseManager; 33 | import com.xargsgrep.portknocker.model.Host; 34 | 35 | import java.util.List; 36 | 37 | public class ConfigureWidgetHostArrayAdapter extends ArrayAdapter 38 | { 39 | private DatabaseManager databaseManager; 40 | private Context context; 41 | private List hosts; 42 | private int appWidgetId; 43 | 44 | public ConfigureWidgetHostArrayAdapter(Context context, List hosts, int appWidgetId) 45 | { 46 | super(context, -1, hosts); 47 | databaseManager = new DatabaseManager(context); 48 | this.context = context; 49 | this.hosts = hosts; 50 | this.appWidgetId = appWidgetId; 51 | } 52 | 53 | @Override 54 | public int getCount() 55 | { 56 | return hosts.size(); 57 | } 58 | 59 | @Override 60 | public Host getItem(int position) 61 | { 62 | return hosts.get(position); 63 | } 64 | 65 | @Override 66 | public View getView(int position, View convertView, ViewGroup parent) 67 | { 68 | View view = convertView; 69 | if (view == null) view = LayoutInflater.from(getContext()).inflate(R.layout.host_row, null); 70 | 71 | TextView labelView = (TextView) view.findViewById(R.id.host_row_label); 72 | TextView hostnameView = (TextView) view.findViewById(R.id.host_row_hostname); 73 | TextView portsView = (TextView) view.findViewById(R.id.host_row_ports); 74 | 75 | Host host = hosts.get(position); 76 | 77 | labelView.setText(host.getLabel()); 78 | hostnameView.setText(host.getHostname()); 79 | portsView.setText(host.getPortsString()); 80 | 81 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 82 | if (sharedPreferences.getBoolean(context.getString(R.string.pref_key_hide_ports), false)) 83 | { 84 | portsView.setVisibility(View.GONE); 85 | } 86 | 87 | final int fPosition = position; 88 | view.setOnClickListener(new OnClickListener() 89 | { 90 | @Override 91 | public void onClick(View v) 92 | { 93 | long hostId = getItem(fPosition).getId(); 94 | ConfigureWidgetActivity.saveHostIdPreference(context, appWidgetId, hostId); 95 | // workaround for phantom widgets 96 | ConfigureWidgetActivity.saveConfiguredPreference(context, appWidgetId); 97 | 98 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 99 | HostWidget.updateAppWidget(context, appWidgetManager, appWidgetId, hostId); 100 | 101 | Intent resultValue = new Intent(); 102 | resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 103 | ((Activity) context).setResult(Activity.RESULT_OK, resultValue); 104 | ((Activity) context).finish(); 105 | } 106 | }); 107 | 108 | view.findViewById(R.id.host_row_delete).setVisibility(View.GONE); 109 | view.findViewById(R.id.host_row_edit).setVisibility(View.GONE); 110 | view.findViewById(R.id.host_row_divider1).setVisibility(View.GONE); 111 | view.findViewById(R.id.host_row_divider2).setVisibility(View.GONE); 112 | 113 | return view; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Port Knocker 4 | Port Knocker 1x1 5 | Port Knocker 2x1 6 | Port Knocker 3x1 7 | Port Knocker 4x1 8 | Host 9 | Ports 10 | Weitere Einstellungen 11 | Titel 12 | Titel 13 | Hostname/IP 14 | domain.com/192.168.10.1 15 | Port 16 | 1234 17 | Protokoll 18 | Verzögerung zwischen Paketen (ms) 19 | 1000 20 | Starte Applikation 21 | Keine 22 | TCP Verbindungs-Timeout (ms) 23 | Ja 24 | Nein 25 | Host löschen? 26 | Änderungen verwerfen? 27 | Empfange Applikation… 28 | Sende Pakete… 29 | hide_ports 30 | Ports in Hostliste verstecken 31 | Portsequenz nicht in der Hostliste anzeigen 32 | hide_ports_widget 33 | Ports im Widget verstecken 34 | Portsequenz nicht im Widget anzeigen 35 | Weitere Einstellungen 36 | Einstellungen 37 | Liste ist leer 38 | \"Es können nicht alle Ports gelöscht werden\" 39 | Bitte Titel eingeben 40 | Hostnamen eingeben 41 | Ungültiger hostname/IP 42 | Es muss mindestens ein Port angegeben werden 43 | "Verzögerung kann nicht größer sein als " 44 | "Ungültiger Port: " 45 | Gespeichert 46 | Speichern fehlgeschlagen 47 | Anklopfen komplett 48 | Anklopfen abgebrochen 49 | \"Anklopfen fehlgeschlagen: \" 50 | "Importiert aus: " 51 | "Import fehlgeschlagen: " 52 | Keine Rechte um auf externen Speicher zuzugreifen. 53 | Einstellungen 54 | Exportiere Hostliste 55 | Importiere Hostliste 56 | Sende Hostliste 57 | Sortiere Hostliste 58 | Nach Hostname 59 | Nach Titel 60 | Nach Datum 61 | Exportiere Hosts 62 | Dateiname für Export 63 | Ok 64 | Abbrechen 65 | "Hostliste exportiert in file: " 66 | "Exportieren von Hostliste fehlgeschlagen: " 67 | "Teile mit... " 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/fragment/PortsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.fragment; 17 | 18 | import android.os.Bundle; 19 | import android.support.v4.app.ListFragment; 20 | import android.view.LayoutInflater; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | import android.widget.ImageButton; 24 | import android.widget.LinearLayout; 25 | import android.widget.ListView; 26 | 27 | import com.xargsgrep.portknocker.R; 28 | import com.xargsgrep.portknocker.activity.EditHostActivity; 29 | import com.xargsgrep.portknocker.adapter.PortArrayAdapter; 30 | import com.xargsgrep.portknocker.db.DatabaseManager; 31 | import com.xargsgrep.portknocker.model.Host; 32 | import com.xargsgrep.portknocker.model.Port; 33 | 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | public class PortsFragment extends ListFragment 38 | { 39 | public static final String TAG = "PortsFragment"; 40 | 41 | private DatabaseManager databaseManager; 42 | private PortArrayAdapter portAdapter; 43 | private boolean savedInstanceState = false; 44 | 45 | public static PortsFragment newInstance(Long hostId) 46 | { 47 | PortsFragment fragment = new PortsFragment(); 48 | if (hostId != null) 49 | { 50 | Bundle args = new Bundle(); 51 | args.putLong(EditHostActivity.KEY_HOST_ID, hostId); 52 | fragment.setArguments(args); 53 | } 54 | return fragment; 55 | } 56 | 57 | @Override 58 | public void onCreate(Bundle savedInstanceState) 59 | { 60 | super.onCreate(savedInstanceState); 61 | setRetainInstance(true); 62 | setHasOptionsMenu(true); 63 | databaseManager = new DatabaseManager(getActivity()); 64 | } 65 | 66 | @Override 67 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 68 | { 69 | super.onCreateView(inflater, container, savedInstanceState); 70 | View view = inflater.inflate(R.layout.list_view, container, false); 71 | View header = inflater.inflate(R.layout.ports_header, null); 72 | ((LinearLayout) view.findViewById(R.id.list_header)).addView(header); 73 | return view; 74 | } 75 | 76 | @Override 77 | public void onViewCreated(View view, Bundle savedInstanceState) 78 | { 79 | super.onViewCreated(view, savedInstanceState); 80 | 81 | ImageButton addButton = (ImageButton) view.findViewById(R.id.fab_image_button); 82 | addButton.setOnClickListener(new View.OnClickListener() 83 | { 84 | @Override 85 | public void onClick(View v) 86 | { 87 | addPort(); 88 | } 89 | }); 90 | 91 | Bundle args = getArguments(); 92 | 93 | List defaultPorts = new ArrayList<>(); 94 | defaultPorts.add(new Port()); 95 | defaultPorts.add(new Port()); 96 | defaultPorts.add(new Port()); 97 | 98 | if (args != null && !this.savedInstanceState) 99 | { 100 | // only restore state from args if onSaveInstanceState hasn't been invoked 101 | Long hostId = args.getLong(EditHostActivity.KEY_HOST_ID); 102 | Host host = databaseManager.getHost(hostId); 103 | List ports = (host.getPorts().size() > 0) ? host.getPorts() : defaultPorts; 104 | portAdapter = new PortArrayAdapter(getActivity(), ports); 105 | setListAdapter(portAdapter); 106 | } 107 | else if (portAdapter == null) 108 | { 109 | portAdapter = new PortArrayAdapter(getActivity(), defaultPorts); 110 | setListAdapter(portAdapter); 111 | } 112 | } 113 | 114 | @Override 115 | public void onSaveInstanceState(Bundle outState) 116 | { 117 | super.onSaveInstanceState(outState); 118 | savedInstanceState = true; 119 | } 120 | 121 | public void clearFoci() 122 | { 123 | ListView view = getListView(); 124 | for (int i = 0; i < view.getChildCount(); i++) 125 | { 126 | View row = view.getChildAt(i); 127 | row.findViewById(R.id.port_row_port).clearFocus(); 128 | } 129 | } 130 | 131 | private void addPort() 132 | { 133 | PortArrayAdapter adapter = (PortArrayAdapter) getListAdapter(); 134 | adapter.add(new Port()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/asynctask/KnockerAsyncTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.asynctask; 17 | 18 | import android.app.ProgressDialog; 19 | import android.content.Intent; 20 | import android.os.AsyncTask; 21 | import android.support.v4.app.Fragment; 22 | import android.support.v4.app.FragmentActivity; 23 | import android.support.v4.app.FragmentManager; 24 | import android.support.v4.app.FragmentTransaction; 25 | import android.view.Gravity; 26 | import android.widget.Toast; 27 | 28 | import com.xargsgrep.portknocker.R; 29 | import com.xargsgrep.portknocker.asynctask.Knocker.KnockResult; 30 | import com.xargsgrep.portknocker.fragment.ProgressDialogFragment; 31 | import com.xargsgrep.portknocker.model.Host; 32 | import com.xargsgrep.portknocker.utils.StringUtils; 33 | 34 | public class KnockerAsyncTask extends AsyncTask 35 | { 36 | private FragmentActivity activity; 37 | private int progressMax; 38 | 39 | public KnockerAsyncTask(FragmentActivity activity, int progressMax) 40 | { 41 | this.activity = activity; 42 | this.progressMax = progressMax; 43 | } 44 | 45 | @Override 46 | protected void onPreExecute() 47 | { 48 | FragmentManager fragmentManager = activity.getSupportFragmentManager(); 49 | 50 | FragmentTransaction ft = fragmentManager.beginTransaction(); 51 | Fragment prev = fragmentManager.findFragmentByTag(ProgressDialogFragment.TAG); 52 | if (prev != null) ft.remove(prev); 53 | ft.addToBackStack(null); 54 | 55 | ProgressDialogFragment dialogFragment = ProgressDialogFragment.newInstance(this, activity.getString(R.string.progress_dialog_sending_packets), false, ProgressDialog.STYLE_HORIZONTAL, progressMax); 56 | dialogFragment.setCancelable(true); 57 | dialogFragment.show(ft, ProgressDialogFragment.TAG); 58 | } 59 | 60 | @Override 61 | protected KnockResult doInBackground(Host... params) 62 | { 63 | Host host = params[0]; 64 | // pass in 'this' so the progress dialog can be updated 65 | return Knocker.doKnock(host, this); 66 | } 67 | 68 | @Override 69 | protected void onPostExecute(KnockResult result) 70 | { 71 | FragmentManager fragmentManager = activity.getSupportFragmentManager(); 72 | 73 | Fragment dialog = fragmentManager.findFragmentByTag(ProgressDialogFragment.TAG); 74 | if (dialog != null) ((ProgressDialogFragment) dialog).dismiss(); 75 | 76 | if (result.isSuccess()) 77 | { 78 | if (StringUtils.isNotBlank(result.getLaunchIntentPackage())) 79 | { 80 | Intent launchIntent = activity.getPackageManager().getLaunchIntentForPackage(result.getLaunchIntentPackage()); 81 | if (launchIntent == null) 82 | { 83 | showToast("Unable to launch intent " + result.getLaunchIntentPackage()); 84 | } 85 | else 86 | { 87 | activity.startActivity(launchIntent); 88 | } 89 | } 90 | else 91 | { 92 | showToast(activity.getString(R.string.toast_msg_knocking_complete)); 93 | } 94 | } 95 | else 96 | { 97 | showToast(activity.getString(R.string.toast_msg_knocking_failed) + result.getError()); 98 | } 99 | } 100 | 101 | @Override 102 | protected void onCancelled() 103 | { 104 | super.onCancelled(); 105 | showToast(activity.getString(R.string.toast_msg_knocking_canceled)); 106 | } 107 | 108 | @Override 109 | protected void onProgressUpdate(Integer... values) 110 | { 111 | FragmentManager fragmentManager = activity.getSupportFragmentManager(); 112 | Fragment dialogFragment = fragmentManager.findFragmentByTag(ProgressDialogFragment.TAG); 113 | if (dialogFragment != null) 114 | ((ProgressDialogFragment) dialogFragment).setProgress(values[0]); 115 | } 116 | 117 | public void doPublishProgress(Integer value) 118 | { 119 | publishProgress(value); 120 | } 121 | 122 | private void showToast(String text) 123 | { 124 | Toast toast = Toast.makeText(activity, text, Toast.LENGTH_LONG); 125 | toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0); 126 | toast.show(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/fragment/HostFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.fragment; 17 | 18 | import android.os.Bundle; 19 | import android.support.v4.app.Fragment; 20 | import android.text.Editable; 21 | import android.text.InputFilter; 22 | import android.text.TextWatcher; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.EditText; 27 | 28 | import com.xargsgrep.portknocker.R; 29 | import com.xargsgrep.portknocker.activity.EditHostActivity; 30 | import com.xargsgrep.portknocker.db.DatabaseManager; 31 | import com.xargsgrep.portknocker.filter.HostnameInputFilter; 32 | import com.xargsgrep.portknocker.model.Host; 33 | 34 | public class HostFragment extends Fragment 35 | { 36 | public static final String TAG = "HostFragment"; 37 | 38 | private static final InputFilter HOSTNAME_INPUT_FILTER = new HostnameInputFilter(); 39 | 40 | private DatabaseManager databaseManager; 41 | private String hostLabel; 42 | private String hostname; 43 | 44 | public static HostFragment newInstance(Long hostId) 45 | { 46 | HostFragment fragment = new HostFragment(); 47 | if (hostId != null) 48 | { 49 | Bundle args = new Bundle(); 50 | args.putLong(EditHostActivity.KEY_HOST_ID, hostId); 51 | fragment.setArguments(args); 52 | } 53 | return fragment; 54 | } 55 | 56 | @Override 57 | public void onCreate(Bundle savedInstanceState) 58 | { 59 | super.onCreate(savedInstanceState); 60 | setRetainInstance(true); 61 | databaseManager = new DatabaseManager(getActivity()); 62 | } 63 | 64 | @Override 65 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 66 | { 67 | super.onCreateView(inflater, container, savedInstanceState); 68 | return inflater.inflate(R.layout.host_fragment, container, false); 69 | } 70 | 71 | @Override 72 | public void onViewCreated(View view, Bundle savedInstanceState) 73 | { 74 | super.onViewCreated(view, savedInstanceState); 75 | 76 | EditText hostLabelEdit = getHostLabelEditText(); 77 | EditText hostnameEdit = getHostnameEditText(); 78 | 79 | hostLabelEdit.addTextChangedListener(new FieldSettingTextWatcher("hostLabel")); 80 | hostnameEdit.addTextChangedListener(new FieldSettingTextWatcher("hostname")); 81 | 82 | Bundle args = getArguments(); 83 | 84 | if (savedInstanceState != null) 85 | { 86 | hostLabelEdit.setText(savedInstanceState.getString("hostLabel")); 87 | hostnameEdit.setText(savedInstanceState.getString("hostname")); 88 | } 89 | else if (args != null) 90 | { 91 | Long hostId = args.getLong(EditHostActivity.KEY_HOST_ID); 92 | Host host = databaseManager.getHost(hostId); 93 | hostLabelEdit.setText(host.getLabel()); 94 | hostnameEdit.setText(host.getHostname()); 95 | } 96 | 97 | hostnameEdit.setFilters(new InputFilter[] {HOSTNAME_INPUT_FILTER}); 98 | } 99 | 100 | @Override 101 | public void onSaveInstanceState(Bundle outState) 102 | { 103 | super.onSaveInstanceState(outState); 104 | outState.putString("hostLabel", hostLabel); 105 | outState.putString("hostname", hostname); 106 | } 107 | 108 | public String getHostLabel() 109 | { 110 | return hostLabel; 111 | } 112 | 113 | public String getHostname() 114 | { 115 | return hostname; 116 | } 117 | 118 | private EditText getHostLabelEditText() 119 | { 120 | return (EditText) getView().findViewById(R.id.host_label_edit); 121 | } 122 | 123 | private EditText getHostnameEditText() 124 | { 125 | return (EditText) getView().findViewById(R.id.host_name_edit); 126 | } 127 | 128 | private class FieldSettingTextWatcher implements TextWatcher 129 | { 130 | private String field; 131 | 132 | private FieldSettingTextWatcher(String field) 133 | { 134 | this.field = field; 135 | } 136 | 137 | @Override 138 | public void afterTextChanged(Editable s) 139 | { 140 | if (field.equals("hostLabel")) 141 | { 142 | hostLabel = s.toString(); 143 | } 144 | else if (field.equals("hostname")) 145 | { 146 | hostname = s.toString(); 147 | } 148 | } 149 | 150 | @Override 151 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { } 152 | 153 | @Override 154 | public void onTextChanged(CharSequence s, int start, int before, int count) { } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/adapter/PortArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.adapter; 17 | 18 | import android.content.Context; 19 | import android.view.Gravity; 20 | import android.view.LayoutInflater; 21 | import android.view.View; 22 | import android.view.View.OnClickListener; 23 | import android.view.View.OnFocusChangeListener; 24 | import android.view.ViewGroup; 25 | import android.widget.AdapterView; 26 | import android.widget.AdapterView.OnItemSelectedListener; 27 | import android.widget.ArrayAdapter; 28 | import android.widget.EditText; 29 | import android.widget.ImageButton; 30 | import android.widget.LinearLayout; 31 | import android.widget.Spinner; 32 | import android.widget.Toast; 33 | 34 | import com.xargsgrep.portknocker.R; 35 | import com.xargsgrep.portknocker.model.Port; 36 | import com.xargsgrep.portknocker.model.Port.Protocol; 37 | import com.xargsgrep.portknocker.utils.StringUtils; 38 | 39 | import java.util.List; 40 | 41 | public class PortArrayAdapter extends ArrayAdapter 42 | { 43 | private Context context; 44 | private List ports; 45 | 46 | public PortArrayAdapter(Context context, List ports) 47 | { 48 | super(context, -1, ports); 49 | this.context = context; 50 | this.ports = ports; 51 | } 52 | 53 | @Override 54 | public int getCount() 55 | { 56 | return ports.size(); 57 | } 58 | 59 | @Override 60 | public Port getItem(int position) 61 | { 62 | return ports.get(position); 63 | } 64 | 65 | public List getPorts() 66 | { 67 | return ports; 68 | } 69 | 70 | @Override 71 | public View getView(int position, View convertView, ViewGroup parent) 72 | { 73 | View view = convertView; 74 | if (view == null) view = LayoutInflater.from(getContext()).inflate(R.layout.port_row, null); 75 | 76 | EditText portView = (EditText) view.findViewById(R.id.port_row_port); 77 | Spinner protocolSpinner = (Spinner) view.findViewById(R.id.port_row_protocol); 78 | ImageButton deleteButton = (ImageButton) view.findViewById(R.id.port_row_delete); 79 | 80 | ArrayAdapter protocolAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, Protocol.values()); 81 | protocolAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 82 | protocolSpinner.setAdapter(protocolAdapter); 83 | 84 | Port port = ports.get(position); 85 | portView.setText((port.getPort() > -1) ? Integer.valueOf(port.getPort()).toString() : ""); 86 | protocolSpinner.setSelection(port.getProtocol().ordinal()); 87 | 88 | final int fPosition = position; 89 | portView.setOnFocusChangeListener(new OnFocusChangeListener() 90 | { 91 | @Override 92 | public void onFocusChange(View view, boolean hasFocus) 93 | { 94 | if (!hasFocus) 95 | { 96 | String portStr = ((EditText) view).getText().toString(); 97 | // if fPosition is out of bounds, it means the last row was focused and a row before it was deleted 98 | ports.get((fPosition >= getCount()) 99 | ? fPosition - 1 100 | : fPosition).setPort((StringUtils.isNotBlank(portStr)) ? Integer.parseInt(portStr) : -1); 101 | } 102 | } 103 | }); 104 | 105 | protocolSpinner.setOnItemSelectedListener(new OnItemSelectedListener() 106 | { 107 | @Override 108 | public void onItemSelected(AdapterView parent, View view, int position, long id) 109 | { 110 | ports.get(fPosition).setProtocol(Protocol.values()[position]); 111 | } 112 | 113 | @Override 114 | public void onNothingSelected(AdapterView parent) { } 115 | }); 116 | 117 | deleteButton.setOnClickListener(new OnClickListener() 118 | { 119 | @Override 120 | public void onClick(View v) 121 | { 122 | if (getCount() == 1) 123 | { 124 | Toast toast = Toast.makeText(getContext(), context.getString(R.string.toast_msg_delete_all_ports), Toast.LENGTH_SHORT); 125 | toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0); 126 | toast.show(); 127 | } 128 | else 129 | { 130 | LinearLayout parent = (LinearLayout) v.getParent(); 131 | EditText dPortView = (EditText) parent.findViewById(R.id.port_row_port); 132 | dPortView.setOnFocusChangeListener(null); 133 | remove(getItem(fPosition)); 134 | } 135 | } 136 | }); 137 | 138 | return view; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/widget/HostWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.widget; 17 | 18 | import android.app.PendingIntent; 19 | import android.appwidget.AppWidgetManager; 20 | import android.appwidget.AppWidgetProvider; 21 | import android.content.ComponentName; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.SharedPreferences; 25 | import android.preference.PreferenceManager; 26 | import android.view.View; 27 | import android.widget.RemoteViews; 28 | 29 | import com.xargsgrep.portknocker.R; 30 | import com.xargsgrep.portknocker.activity.HostListActivity; 31 | import com.xargsgrep.portknocker.db.DatabaseManager; 32 | import com.xargsgrep.portknocker.model.Host; 33 | 34 | public class HostWidget extends AppWidgetProvider 35 | { 36 | @Override 37 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 38 | { 39 | for (int appWidgetId : appWidgetIds) 40 | { 41 | long hostId = ConfigureWidgetActivity.getHostIdPreference(context, appWidgetId); 42 | updateAppWidget(context, appWidgetManager, appWidgetId, hostId); 43 | } 44 | } 45 | 46 | @Override 47 | public void onDeleted(Context context, int[] appWidgetIds) 48 | { 49 | for (int i = 0; i < appWidgetIds.length; i++) 50 | { 51 | ConfigureWidgetActivity.deleteHostIdPreference(context, appWidgetIds[i]); 52 | } 53 | } 54 | 55 | public static void updateAllAppWidgets(Context context) 56 | { 57 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 58 | int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, HostWidget.class)); 59 | for (int appWidgetId : appWidgetIds) 60 | { 61 | updateAppWidget(context, appWidgetManager, appWidgetId, null); 62 | } 63 | } 64 | 65 | public static void updateAllAppWidgetsForHost(Context context, long hostId) 66 | { 67 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 68 | int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, HostWidget.class)); 69 | for (int appWidgetId : appWidgetIds) 70 | { 71 | updateAppWidget(context, appWidgetManager, appWidgetId, hostId); 72 | } 73 | } 74 | 75 | public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Long hostId) 76 | { 77 | // workaround for phantom widgets 78 | boolean configured = ConfigureWidgetActivity.getConfiguredPreference(context, appWidgetId); 79 | if (!configured) return; 80 | 81 | Long widgetHostId = ConfigureWidgetActivity.getHostIdPreference(context, appWidgetId); 82 | if (hostId != null && !hostId.equals(widgetHostId)) return; 83 | 84 | DatabaseManager hostDataManager = new DatabaseManager(context); 85 | RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); 86 | 87 | 88 | boolean hostExists = hostDataManager.hostExists(widgetHostId); 89 | if (hostExists) 90 | { 91 | Host host = hostDataManager.getHost(widgetHostId); 92 | 93 | Intent intent = new Intent(context, HostListActivity.class); 94 | intent.putExtra("hostId", widgetHostId); 95 | PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_CANCEL_CURRENT); 96 | views.setOnClickPendingIntent(R.id.widget, pendingIntent); 97 | 98 | views.setTextViewText(R.id.widget_host_label, host.getLabel()); 99 | views.setTextViewText(R.id.widget_host_hostname, host.getHostname()); 100 | views.setTextViewText(R.id.widget_host_ports, host.getPortsString()); 101 | 102 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 103 | if (sharedPreferences.getBoolean(context.getString(R.string.pref_key_hide_ports_widget), false)) 104 | { 105 | views.setViewVisibility(R.id.widget_host_ports, View.GONE); 106 | } 107 | else 108 | { 109 | views.setViewVisibility(R.id.widget_host_ports, View.VISIBLE); 110 | } 111 | } 112 | else 113 | { 114 | views.setTextViewText(R.id.widget_host_label, "Invalid Host"); 115 | views.setViewVisibility(R.id.widget_host_hostname, View.GONE); 116 | views.setViewVisibility(R.id.widget_host_ports, View.GONE); 117 | 118 | Intent intent = new Intent(context, HostListActivity.class); 119 | PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_CANCEL_CURRENT); 120 | views.setOnClickPendingIntent(R.id.widget, pendingIntent); 121 | } 122 | 123 | appWidgetManager.updateAppWidget(appWidgetId, views); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/db/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.db; 17 | 18 | import android.content.Context; 19 | import android.database.sqlite.SQLiteDatabase; 20 | import android.database.sqlite.SQLiteOpenHelper; 21 | import android.provider.BaseColumns; 22 | import android.util.Log; 23 | 24 | import com.xargsgrep.portknocker.model.Host; 25 | 26 | public class DatabaseHelper extends SQLiteOpenHelper 27 | { 28 | private static final String TAG = "DatabaseHelper"; 29 | private static final int DATABASE_VERSION = 2; 30 | 31 | private static final String DATABASE_FILENAME = "hosts.db"; 32 | 33 | public static final String HOST_TABLE_NAME = "host"; 34 | public static final String HOST_ID_COLUMN = BaseColumns._ID; 35 | public static final String HOST_LABEL_COLUMN = "label"; 36 | public static final String HOST_HOSTNAME_COLUMN = "hostname"; 37 | public static final String HOST_DELAY_COLUMN = "delay"; 38 | public static final String HOST_TCP_CONNECT_TIMEOUT_COLUMN = "tcp_connect_timeout"; 39 | public static final String HOST_LAUNCH_INTENT_PACKAGE_COLUMN = "launch_intent_package"; 40 | public static final String HOST_USERNAME_COLUMN = "username"; 41 | public static final String HOST_TARGET_PORT_COLUMN = "target_port"; 42 | 43 | public static final String PORT_TABLE_NAME = "port"; 44 | public static final String PORT_HOST_ID_COLUMN = "host_id"; 45 | public static final String PORT_INDEX_COLUMN = "idx"; 46 | public static final String PORT_PORT_COLUMN = "port"; 47 | public static final String PORT_PROTOCOL_COLUMN = "protocol"; 48 | 49 | public static final String[] HOST_TABLE_COLUMNS = new String[] 50 | { 51 | HOST_ID_COLUMN, 52 | HOST_LABEL_COLUMN, 53 | HOST_HOSTNAME_COLUMN, 54 | HOST_DELAY_COLUMN, 55 | HOST_LAUNCH_INTENT_PACKAGE_COLUMN, 56 | HOST_USERNAME_COLUMN, 57 | HOST_TARGET_PORT_COLUMN, 58 | HOST_TCP_CONNECT_TIMEOUT_COLUMN 59 | }; 60 | public static final String[] PORT_TABLE_COLUMNS = new String[] 61 | { 62 | PORT_HOST_ID_COLUMN, 63 | PORT_INDEX_COLUMN, 64 | PORT_PORT_COLUMN, 65 | PORT_PROTOCOL_COLUMN 66 | }; 67 | 68 | public DatabaseHelper(Context context) 69 | { 70 | super(context, DATABASE_FILENAME, null, DATABASE_VERSION); 71 | } 72 | 73 | @Override 74 | public void onCreate(SQLiteDatabase db) 75 | { 76 | String createHostTableSQL = 77 | "create table %s (" + 78 | " %s integer primary key autoincrement," + 79 | " %s string not null," + 80 | " %s string not null," + 81 | " %s integer not null default %d," + 82 | " %s string," + 83 | " %s string," + 84 | " %s integer," + 85 | " %s integer not null default %d" + 86 | ");"; 87 | createHostTableSQL = String.format( 88 | createHostTableSQL, 89 | HOST_TABLE_NAME, 90 | HOST_ID_COLUMN, 91 | HOST_LABEL_COLUMN, 92 | HOST_HOSTNAME_COLUMN, 93 | HOST_DELAY_COLUMN, Host.DEFAULT_DELAY, 94 | HOST_LAUNCH_INTENT_PACKAGE_COLUMN, 95 | HOST_USERNAME_COLUMN, 96 | HOST_TARGET_PORT_COLUMN, 97 | HOST_TCP_CONNECT_TIMEOUT_COLUMN, Host.DEFAULT_TCP_CONNECT_TIMEOUT 98 | ); 99 | 100 | String createPortTableSQL = 101 | "create table %s (" + 102 | " %s integer not null," + 103 | " %s integer not null," + 104 | " %s integer not null," + 105 | " %s integer not null" + 106 | ");"; 107 | createPortTableSQL = String.format( 108 | createPortTableSQL, 109 | PORT_TABLE_NAME, 110 | PORT_HOST_ID_COLUMN, 111 | PORT_INDEX_COLUMN, 112 | PORT_PORT_COLUMN, 113 | PORT_PROTOCOL_COLUMN 114 | ); 115 | 116 | db.execSQL(createHostTableSQL); 117 | db.execSQL(createPortTableSQL); 118 | } 119 | 120 | @Override 121 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 122 | { 123 | Log.d(TAG, "Upgrading database from version " + oldVersion + " to version " + newVersion); 124 | switch (newVersion) 125 | { 126 | case DATABASE_VERSION: 127 | db.execSQL(String.format( 128 | "alter table %s add column %s integer not null default %d;", 129 | HOST_TABLE_NAME, 130 | HOST_TCP_CONNECT_TIMEOUT_COLUMN, 131 | Host.DEFAULT_TCP_CONNECT_TIMEOUT 132 | )); 133 | return; 134 | default: 135 | throw new IllegalStateException("Invalid newVersion: " + newVersion); 136 | } 137 | } 138 | } 139 | /* 140 | Version 1: 141 | create table host( 142 | _id integer primary key autoincrement, 143 | label string not null, 144 | hostname string not null, 145 | delay integer not null default 0, 146 | launch_intent_package string, 147 | username string, 148 | target_port integer 149 | ); 150 | 151 | Version 2: 152 | create table host( 153 | _id integer primary key autoincrement, 154 | label string not null, 155 | hostname string not null, 156 | delay integer not null default 1000, 157 | launch_intent_package string, 158 | username string, 159 | target_port integer, 160 | tcp_connect_timeout integer not null default 100, 161 | ); 162 | */ 163 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 81 | 82 | 83 | 84 | 85 | 89 | 90 | 94 | 95 | 96 | 97 | 98 | 102 | 103 | 107 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 142 | 143 | 144 | 145 | 146 | 147 | 152 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/fragment/HostListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.fragment; 17 | 18 | import android.app.AlertDialog; 19 | import android.content.DialogInterface; 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.support.v4.app.ListFragment; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.ArrayAdapter; 27 | import android.widget.ImageButton; 28 | 29 | import com.xargsgrep.portknocker.R; 30 | import com.xargsgrep.portknocker.activity.EditHostActivity; 31 | import com.xargsgrep.portknocker.activity.HostListActivity; 32 | import com.xargsgrep.portknocker.adapter.HostArrayAdapter; 33 | import com.xargsgrep.portknocker.db.DatabaseManager; 34 | import com.xargsgrep.portknocker.model.Host; 35 | import com.xargsgrep.portknocker.widget.HostWidget; 36 | 37 | import java.util.Collections; 38 | import java.util.Comparator; 39 | import java.util.List; 40 | 41 | public class HostListFragment extends ListFragment 42 | { 43 | public static final String TAG = "HostListFragment"; 44 | 45 | private DatabaseManager databaseManager; 46 | private int deleteHostPosition; 47 | 48 | public static HostListFragment newInstance() 49 | { 50 | return new HostListFragment(); 51 | } 52 | 53 | @Override 54 | public void onCreate(Bundle savedInstanceState) 55 | { 56 | super.onCreate(savedInstanceState); 57 | setRetainInstance(true); 58 | databaseManager = new DatabaseManager(getActivity()); 59 | } 60 | 61 | @Override 62 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 63 | { 64 | super.onCreateView(inflater, container, savedInstanceState); 65 | return inflater.inflate(R.layout.list_view, container, false); 66 | } 67 | 68 | @Override 69 | public void onViewCreated(View view, Bundle savedInstanceState) 70 | { 71 | super.onViewCreated(view, savedInstanceState); 72 | 73 | ImageButton addButton = (ImageButton) view.findViewById(R.id.fab_image_button); 74 | addButton.setOnClickListener(new View.OnClickListener() 75 | { 76 | @Override 77 | public void onClick(View v) 78 | { 79 | Intent editHostIntent = new Intent(getActivity(), EditHostActivity.class); 80 | startActivity(editHostIntent); 81 | } 82 | }); 83 | 84 | List hosts = databaseManager.getAllHosts(); 85 | HostArrayAdapter hostAdapter = new HostArrayAdapter(this, hosts); 86 | setListAdapter(hostAdapter); 87 | } 88 | 89 | public void showDeleteDialog() 90 | { 91 | AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); 92 | dialogBuilder.setTitle(R.string.confirm_dialog_delete_host_title); 93 | dialogBuilder.setIcon(R.drawable.ic_alert); 94 | 95 | final int position = deleteHostPosition; 96 | dialogBuilder.setPositiveButton(R.string.confirm_dialog_confirm, 97 | new DialogInterface.OnClickListener() 98 | { 99 | @SuppressWarnings("unchecked") 100 | public void onClick(DialogInterface dialog, int which) 101 | { 102 | Host host = (Host) getListAdapter().getItem(position); 103 | databaseManager.deleteHost(host); 104 | ((ArrayAdapter) getListAdapter()).remove(host); 105 | HostWidget.updateAllAppWidgetsForHost(getActivity(), host.getId()); 106 | } 107 | } 108 | ); 109 | dialogBuilder.setNegativeButton(R.string.confirm_dialog_cancel, 110 | new DialogInterface.OnClickListener() 111 | { 112 | public void onClick(DialogInterface dialog, int which) { } 113 | } 114 | ); 115 | 116 | AlertDialog deleteDialog = dialogBuilder.create(); 117 | ((HostListActivity) getActivity()).setDeleteDialog(deleteDialog); 118 | deleteDialog.show(); 119 | } 120 | 121 | public void setDeleteHostPosition(int position) 122 | { 123 | this.deleteHostPosition = position; 124 | } 125 | 126 | public void refreshHosts() 127 | { 128 | List hosts = databaseManager.getAllHosts(); 129 | setHosts(hosts); 130 | } 131 | 132 | public void sortByHostname() 133 | { 134 | sortHosts(new Comparator() 135 | { 136 | @Override 137 | public int compare(Host lhs, Host rhs) 138 | { 139 | return lhs.getHostname().compareTo(rhs.getHostname()); 140 | } 141 | }); 142 | } 143 | 144 | public void sortByLabel() 145 | { 146 | sortHosts(new Comparator() 147 | { 148 | @Override 149 | public int compare(Host lhs, Host rhs) 150 | { 151 | return lhs.getLabel().compareTo(rhs.getLabel()); 152 | } 153 | }); 154 | } 155 | 156 | public void sortByNewest() 157 | { 158 | sortHosts(new Comparator() 159 | { 160 | @Override 161 | public int compare(Host lhs, Host rhs) 162 | { 163 | return Long.valueOf(rhs.getId()).compareTo(lhs.getId()); 164 | } 165 | }); 166 | } 167 | 168 | private void sortHosts(Comparator comparator) 169 | { 170 | List hosts = databaseManager.getAllHosts(); 171 | Collections.sort(hosts, comparator); 172 | setHosts(hosts); 173 | } 174 | 175 | private void setHosts(List hosts) 176 | { 177 | ((ArrayAdapter) getListAdapter()).clear(); 178 | 179 | for (Host host : hosts) 180 | { 181 | ((ArrayAdapter) getListAdapter()).add(host); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/java/com/xargsgrep/portknocker/asynctask/Knocker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Ahsan Rabbani 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xargsgrep.portknocker.asynctask; 17 | 18 | import com.xargsgrep.portknocker.model.Host; 19 | import com.xargsgrep.portknocker.model.Port; 20 | import com.xargsgrep.portknocker.model.Port.Protocol; 21 | import com.xargsgrep.portknocker.utils.SocketUtils; 22 | import com.xargsgrep.portknocker.utils.StringUtils; 23 | 24 | import java.io.IOException; 25 | import java.net.ConnectException; 26 | import java.net.DatagramPacket; 27 | import java.net.DatagramSocket; 28 | import java.net.InetSocketAddress; 29 | import java.net.Socket; 30 | import java.net.SocketAddress; 31 | import java.net.SocketException; 32 | import java.net.SocketTimeoutException; 33 | import java.net.UnknownHostException; 34 | 35 | public class Knocker 36 | { 37 | private static final String ENETUNREACH = "ENETUNREACH"; 38 | 39 | public static KnockResult doKnock(Host host, KnockerAsyncTask asyncTask) 40 | { 41 | KnockResult result = new KnockResult(false, null); 42 | for (int i = 0; i < host.getPorts().size(); i++) 43 | { 44 | Port port = host.getPorts().get(i); 45 | result = doKnock(host, port); 46 | 47 | if (result.isSuccess()) 48 | { 49 | result.setLaunchIntentPackage(host.getLaunchIntentPackage()); 50 | asyncTask.doPublishProgress(i + 1); 51 | 52 | if (i < host.getPorts().size() - 1) 53 | { 54 | // no need to sleep after last knock 55 | try 56 | { 57 | Thread.sleep(host.getDelay()); 58 | } 59 | catch (InterruptedException e) 60 | { 61 | if (asyncTask.isCancelled()) break; 62 | } 63 | } 64 | } 65 | else 66 | { 67 | break; 68 | } 69 | } 70 | 71 | return result; 72 | } 73 | 74 | /* 75 | * no network/TCP --> java.net.ConnectException: failed to connect to /1.1.1.1 (port 1234) after 3000ms: connect failed: ENETUNREACH (Network is unreachable) 76 | * no network/UDP --> java.net.SocketException: sendto failed: ENETUNREACH (Network is unreachable) 77 | * 78 | * unable to resolve host/TCP --> java.net.UnknownHostException: Host is unresolved: fakedomain.com 79 | * unable to resolve host/UDP --> java.lang.IllegalArgumentException: Socket address unresolved: fakedomain.com:1234 80 | * 81 | * TCP: 82 | * router/firewall port closed --> java.net.SocketTimeoutException: failed to connect to fakedomain.com/1.1.1.1 (port 1234) after 3000ms 83 | * router/firewall port open, socket closed & REJECT packet --> java.net.ConnectException: failed to connect to fakedomain.com/1.1.1.1 (port 1234) after 3000ms: isConnected failed: ECONNREFUSED (Connection refused) 84 | * router/firewall port open, socket closed & DROP packet --> java.net.SocketTimeoutException: failed to connect to fakedomain.com/1.1.1.1 (port 1234) after 3000ms 85 | * 86 | * UDP: 87 | * router/firewall port closed --> no exception 88 | * router/firewall port open, socket closed & REJECT packet --> no exception 89 | * router/firewall port open, socket closed & DROP packet --> no exception 90 | * 91 | */ 92 | private static KnockResult doKnock(Host host, Port port) 93 | { 94 | SocketAddress socketAddress = new InetSocketAddress(host.getHostname(), port.getPort()); 95 | 96 | Socket socket = null; 97 | DatagramSocket datagramSocket = null; 98 | try 99 | { 100 | if (port.getProtocol() == Protocol.TCP) 101 | { 102 | socket = new Socket(); 103 | // set timeout to the lowest possible value since we just want to transmit a packet, we don't care about receiving a syn-ack. 104 | // this also prevents multiple syn packets being sent (invalidating knock sequence) while waiting for the timeout (if it's high enough) 105 | socket.connect(socketAddress, host.getTcpConnectTimeout()); 106 | } 107 | else 108 | { // PROTOCOL.UDP 109 | datagramSocket = new DatagramSocket(); 110 | byte[] data = new byte[] {0}; 111 | datagramSocket.send(new DatagramPacket(data, data.length, socketAddress)); 112 | } 113 | } 114 | catch (SocketTimeoutException e) 115 | { 116 | // this is ok since the timeout is as low as can be and the remote socket isn't expected to be open anyway 117 | } 118 | catch (ConnectException e) 119 | { 120 | if (StringUtils.contains(e.getMessage(), ENETUNREACH)) 121 | { 122 | // TCP: host unreachable 123 | return new KnockResult(false, e.getMessage()); 124 | } 125 | // ok otherwise 126 | } 127 | catch (UnknownHostException e) 128 | { 129 | // TCP: unable to resolve hostname 130 | return new KnockResult(false, e.getMessage()); 131 | } 132 | catch (IllegalArgumentException e) 133 | { 134 | // UDP: unable to resolve hostname 135 | return new KnockResult(false, e.getMessage()); 136 | } 137 | catch (SocketException e) 138 | { 139 | // UDP: host unreachable 140 | return new KnockResult(false, e.getMessage()); 141 | } 142 | catch (IOException e) 143 | { 144 | return new KnockResult(false, e.getMessage()); 145 | } 146 | finally 147 | { 148 | SocketUtils.closeQuietly(socket); 149 | SocketUtils.closeQuietly(datagramSocket); 150 | } 151 | 152 | return new KnockResult(true, null); 153 | } 154 | 155 | public static class KnockResult 156 | { 157 | private final boolean success; 158 | private final String error; 159 | private String launchIntentPackage; 160 | 161 | public KnockResult(boolean success, String error) 162 | { 163 | this.success = success; 164 | this.error = error; 165 | } 166 | 167 | public boolean isSuccess() 168 | { 169 | return success; 170 | } 171 | 172 | public String getError() 173 | { 174 | return error; 175 | } 176 | 177 | public String getLaunchIntentPackage() 178 | { 179 | return launchIntentPackage; 180 | } 181 | 182 | public void setLaunchIntentPackage(String launchIntentPackage) 183 | { 184 | this.launchIntentPackage = launchIntentPackage; 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /FileChooser/src/main/java/com/ipaulpro/afilechooser/FileChooserActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Paul Burke 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.ipaulpro.afilechooser; 18 | 19 | import android.app.ActionBar; 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.content.IntentFilter; 24 | import android.net.Uri; 25 | import android.os.Build; 26 | import android.os.Bundle; 27 | import android.os.Environment; 28 | import android.support.v4.app.FragmentActivity; 29 | import android.support.v4.app.FragmentManager; 30 | import android.support.v4.app.FragmentManager.BackStackEntry; 31 | import android.support.v4.app.FragmentManager.OnBackStackChangedListener; 32 | import android.support.v4.app.FragmentTransaction; 33 | import android.support.v4.content.FileProvider; 34 | import android.view.Menu; 35 | import android.view.MenuItem; 36 | import android.widget.Toast; 37 | 38 | import java.io.File; 39 | 40 | /** 41 | * Main Activity that handles the FileListFragments 42 | * 43 | * @version 2013-06-25 44 | * @author paulburke (ipaulpro) 45 | */ 46 | public class FileChooserActivity extends FragmentActivity implements 47 | OnBackStackChangedListener, FileListFragment.Callbacks { 48 | 49 | public static final String PATH = "path"; 50 | public static final String EXTERNAL_BASE_PATH = Environment 51 | .getExternalStorageDirectory().getAbsolutePath(); 52 | 53 | private static final boolean HAS_ACTIONBAR = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; 54 | 55 | private FragmentManager mFragmentManager; 56 | private BroadcastReceiver mStorageListener = new BroadcastReceiver() { 57 | @Override 58 | public void onReceive(Context context, Intent intent) { 59 | Toast.makeText(context, R.string.storage_removed, Toast.LENGTH_LONG).show(); 60 | finishWithResult(null); 61 | } 62 | }; 63 | 64 | private String mPath; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | 70 | mFragmentManager = getSupportFragmentManager(); 71 | mFragmentManager.addOnBackStackChangedListener(this); 72 | 73 | if (savedInstanceState == null) { 74 | mPath = EXTERNAL_BASE_PATH; 75 | addFragment(); 76 | } else { 77 | mPath = savedInstanceState.getString(PATH); 78 | } 79 | 80 | setTitle(mPath); 81 | } 82 | 83 | @Override 84 | protected void onPause() { 85 | super.onPause(); 86 | 87 | unregisterStorageListener(); 88 | } 89 | 90 | @Override 91 | protected void onResume() { 92 | super.onResume(); 93 | 94 | registerStorageListener(); 95 | } 96 | 97 | @Override 98 | protected void onSaveInstanceState(Bundle outState) { 99 | super.onSaveInstanceState(outState); 100 | 101 | outState.putString(PATH, mPath); 102 | } 103 | 104 | @Override 105 | public void onBackStackChanged() { 106 | 107 | int count = mFragmentManager.getBackStackEntryCount(); 108 | if (count > 0) { 109 | BackStackEntry fragment = mFragmentManager.getBackStackEntryAt(count - 1); 110 | mPath = fragment.getName(); 111 | } else { 112 | mPath = EXTERNAL_BASE_PATH; 113 | } 114 | 115 | setTitle(mPath); 116 | if (HAS_ACTIONBAR) 117 | invalidateOptionsMenu(); 118 | } 119 | 120 | @Override 121 | public boolean onCreateOptionsMenu(Menu menu) { 122 | if (HAS_ACTIONBAR) { 123 | boolean hasBackStack = mFragmentManager.getBackStackEntryCount() > 0; 124 | 125 | ActionBar actionBar = getActionBar(); 126 | actionBar.setDisplayHomeAsUpEnabled(hasBackStack); 127 | actionBar.setHomeButtonEnabled(hasBackStack); 128 | } 129 | 130 | return true; 131 | } 132 | 133 | @Override 134 | public boolean onOptionsItemSelected(MenuItem item) { 135 | switch (item.getItemId()) { 136 | case android.R.id.home: 137 | mFragmentManager.popBackStack(); 138 | return true; 139 | } 140 | 141 | return super.onOptionsItemSelected(item); 142 | } 143 | 144 | /** 145 | * Add the initial Fragment with given path. 146 | */ 147 | private void addFragment() { 148 | FileListFragment fragment = FileListFragment.newInstance(mPath); 149 | mFragmentManager.beginTransaction() 150 | .add(android.R.id.content, fragment).commit(); 151 | } 152 | 153 | /** 154 | * "Replace" the existing Fragment with a new one using given path. We're 155 | * really adding a Fragment to the back stack. 156 | * 157 | * @param file The file (directory) to display. 158 | */ 159 | private void replaceFragment(File file) { 160 | mPath = file.getAbsolutePath(); 161 | 162 | FileListFragment fragment = FileListFragment.newInstance(mPath); 163 | mFragmentManager.beginTransaction() 164 | .replace(android.R.id.content, fragment) 165 | .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) 166 | .addToBackStack(mPath).commit(); 167 | } 168 | 169 | /** 170 | * Finish this Activity with a result code and URI of the selected file. 171 | * 172 | * @param file The file selected. 173 | */ 174 | private void finishWithResult(File file) { 175 | if (file != null) { 176 | Uri uri = FileProvider.getUriForFile(this.getBaseContext(), "com.ipaulpro.afilechooser", file); 177 | setResult(RESULT_OK, new Intent().setData(uri)); 178 | finish(); 179 | } else { 180 | setResult(RESULT_CANCELED); 181 | finish(); 182 | } 183 | } 184 | 185 | /** 186 | * Called when the user selects a File 187 | * 188 | * @param file The file that was selected 189 | */ 190 | @Override 191 | public void onFileSelected(File file) { 192 | if (file != null) { 193 | if (file.isDirectory()) { 194 | replaceFragment(file); 195 | } else { 196 | finishWithResult(file); 197 | } 198 | } else { 199 | Toast.makeText(FileChooserActivity.this, R.string.error_selecting_file, 200 | Toast.LENGTH_SHORT).show(); 201 | } 202 | } 203 | 204 | /** 205 | * Register the external storage BroadcastReceiver. 206 | */ 207 | private void registerStorageListener() { 208 | IntentFilter filter = new IntentFilter(); 209 | filter.addAction(Intent.ACTION_MEDIA_REMOVED); 210 | registerReceiver(mStorageListener, filter); 211 | } 212 | 213 | /** 214 | * Unregister the external storage BroadcastReceiver. 215 | */ 216 | private void unregisterStorageListener() { 217 | unregisterReceiver(mStorageListener); 218 | } 219 | } 220 | --------------------------------------------------------------------------------