├── LICENSE.txt ├── README.md ├── org.hive2hive.android.deployment ├── .gitignore ├── assembly.xml ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── hive2hive │ │ └── android │ │ └── deployment │ │ ├── MultiStableH2HPeers.java │ │ └── StableH2HPeer.java │ └── resources │ ├── deployment-single.conf │ ├── deployment.conf │ └── logback.xml └── org.hive2hive.mobile ├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── nrutishauser.xml ├── encodings.xml ├── gradle.xml ├── libraries │ ├── apktool_lib_1_4_4_3.xml │ ├── appcompat_v7_20_0_0.xml │ ├── commons_io_2_4.xml │ ├── core_1_51_0_0.xml │ ├── el_api_2_2.xml │ ├── fst_2_23.xml │ ├── gcm_server_1_0_2.xml │ ├── javassist_3_18_1_GA.xml │ ├── json_simple_1_1.xml │ ├── logback_android_classic_1_1_1_3.xml │ ├── logback_android_core_1_1_1_3.xml │ ├── mbassador_1_2_0.xml │ ├── netty_buffer_4_0_25_Final.xml │ ├── netty_common_4_0_25_Final.xml │ ├── netty_transport_4_0_25_Final.xml │ ├── objenesis_2_1.xml │ ├── org_hive2hive_core_1_2_2.xml │ ├── org_hive2hive_processframework_1_1.xml │ ├── play_services_4_2_42.xml │ ├── prov_1_51_0_0.xml │ ├── slf4j_api_1_7_6.xml │ ├── support_annotations_20_0_0.xml │ ├── support_v4_20_0_0.xml │ ├── tomp2p_android_5_0_Beta5.xml │ ├── tomp2p_core_5_0_Beta5.xml │ ├── tomp2p_dht_5_0_Beta5.xml │ ├── tomp2p_nat_5_0_Beta5.xml │ └── tomp2p_replication_5_0_Beta5.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── workspace.xml ├── app ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── hive2hive │ │ └── mobile │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── logback.xml │ ├── java │ └── org │ │ └── hive2hive │ │ └── mobile │ │ ├── H2HApplication.java │ │ ├── common │ │ ├── AndroidFileAgent.java │ │ ├── ApplicationHelper.java │ │ ├── BaseProgressTask.java │ │ ├── ConnectionMode.java │ │ ├── ISuccessFailListener.java │ │ ├── RelayMode.java │ │ └── UserPermissionComparator.java │ │ ├── connection │ │ ├── ConnectActivity.java │ │ ├── ConnectionChangeListener.java │ │ ├── ConnectionSetupTask.java │ │ └── DisconnectTask.java │ │ ├── files │ │ ├── AndroidFile.java │ │ ├── AndroidFileComparator.java │ │ ├── AndroidFileEventListener.java │ │ ├── FileArrayAdapter.java │ │ ├── FileState.java │ │ ├── FilesActivity.java │ │ ├── FilesFragment.java │ │ └── tasks │ │ │ ├── BaseFileTask.java │ │ │ ├── FileDeleteTask.java │ │ │ ├── FileDownloadTask.java │ │ │ ├── FileListTask.java │ │ │ ├── FileShareTask.java │ │ │ ├── FileUpdateTask.java │ │ │ └── FileUploadTask.java │ │ ├── gcm │ │ ├── GCMBroadcastReceiver.java │ │ ├── GCMIntentService.java │ │ └── GCMRegistrationUtil.java │ │ ├── login │ │ ├── LoginActivity.java │ │ ├── UserLoginTask.java │ │ └── UserLogoutTask.java │ │ ├── preference │ │ ├── PortPickerPreference.java │ │ ├── SettingsActivity.java │ │ └── SettingsFragment.java │ │ └── security │ │ ├── SCSecurityClassProvider.java │ │ ├── SCStrongAESEncryption.java │ │ └── SpongyCastleEncryption.java │ └── res │ ├── animator │ ├── slide_in_left.xml │ ├── slide_in_right.xml │ ├── slide_out_left.xml │ └── slide_out_right.xml │ ├── drawable-hdpi │ ├── ic_action_refresh.png │ ├── ic_action_upload.png │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── ic_action_refresh.png │ ├── ic_action_upload.png │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── ic_action_refresh.png │ ├── ic_action_upload.png │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── ic_action_refresh.png │ ├── ic_action_upload.png │ └── ic_launcher.png │ ├── drawable-xxxhdpi │ └── ic_launcher.png │ ├── drawable │ ├── h2h_logo_large.png │ ├── ic_archive_ex.png │ ├── ic_archive_inex.png │ ├── ic_audio_ex.png │ ├── ic_audio_inex.png │ ├── ic_blank_ex.png │ ├── ic_blank_inex.png │ ├── ic_calendar_ex.png │ ├── ic_calendar_inex.png │ ├── ic_cloud_inex.png │ ├── ic_code_ex.png │ ├── ic_code_inex.png │ ├── ic_download_inex.png │ ├── ic_folder.png │ ├── ic_folder_loading.png │ ├── ic_folder_shared.png │ ├── ic_folder_upload.png │ ├── ic_image_ex.png │ ├── ic_image_inex.png │ ├── ic_loading_inex.png │ ├── ic_powerpoint_ex.png │ ├── ic_powerpoint_inex.png │ ├── ic_spreadsheet_ex.png │ ├── ic_spreadsheet_inex.png │ ├── ic_text_ex.png │ ├── ic_text_inex.png │ ├── ic_upload_ex.png │ ├── ic_video_ex.png │ ├── ic_video_inex.png │ └── login_lock.png │ ├── layout │ ├── activity_connect.xml │ ├── activity_files.xml │ ├── activity_login.xml │ ├── dialog_share.xml │ └── fragment_files.xml │ ├── menu │ ├── menu_files.xml │ └── menu_settings.xml │ ├── values-w820dp │ └── dimens.xml │ ├── values │ ├── dimens.xml │ ├── strings.xml │ ├── strings_activity_connect.xml │ ├── strings_activity_files.xml │ ├── strings_activity_login.xml │ ├── strings_activity_power.xml │ ├── strings_activity_preferences.xml │ └── styles.xml │ └── xml │ └── preferences.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── org.hive2hive.mobile.iml └── settings.gradle /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nico Rutishauser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hive2Hive Android App 2 | ===================== 3 | 4 | In this repository, you can find the sources of the **Hive2Hive Android App** ([Play Store](https://play.google.com/store/apps/details?id=org.hive2hive.mobile)) and simple runnable to create a network with 5 initial peers on a computer or server. 5 | 6 | Also visit the [Wiki](https://github.com/Hive2Hive/Android/wiki) to get more information. 7 | 8 | We appreciate every contribution to the application or the [Hive2Hive core project](https://github.com/Hive2Hive/Hive2Hive) itself. You could for example add translations for your language, improve the usability or stability. Feel free to ask questions or write an issue if something is unclear. 9 | 10 | ## Screenshots 11 | ![Screenshots](http://hive2hive.com/other_content/Android/Collage.png) 12 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse # 2 | 3 | /bin 4 | /target 5 | /logs 6 | .project 7 | .classpath 8 | .settings 9 | 10 | # IntelliJ IDEA # 11 | .idea 12 | *.iml 13 | 14 | # Mac # 15 | 16 | .DS_Store -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | client 4 | 5 | zip 6 | 7 | 8 | 9 | 10 | 11 | 13 | true 14 | lib 15 | false 16 | 17 | 18 | 19 | 20 | 21 | 22 | ${project.build.directory} 23 | 24 | 25 | *.jar 26 | 27 | 28 | 29 | 30 | src/main/resources 31 | 32 | 33 | deployment.conf 34 | logback.xml 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.hive2hive 5 | org.hive2hive.android.deployment 6 | 1.0.2 7 | 8 | 9 | 10 | org.hive2hive 11 | org.hive2hive.core 12 | 1.2.2 13 | 14 | 15 | net.tomp2p 16 | tomp2p-android 17 | 5.0-Beta5 18 | compile 19 | 20 | 21 | ch.qos.logback 22 | logback-classic 23 | 1.1.1 24 | compile 25 | 26 | 27 | com.typesafe 28 | config 29 | 1.2.1 30 | compile 31 | 32 | 33 | 34 | 35 | 36 | hive2hive.org 37 | http://repo.hive2hive.org 38 | 39 | 40 | 41 | tomp2p.net 42 | TomP2P Repository 43 | http://tomp2p.net/dev/mvn/ 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | org.apache.maven.plugins 53 | maven-jar-plugin 54 | 2.5 55 | 56 | 57 | 58 | org.hive2hive.android.deployment.MultiStableH2HPeers 59 | true 60 | lib/ 61 | 62 | 63 | ./ 64 | 65 | 66 | 67 | deployment-single.conf 68 | deployment.conf 69 | logback.xml 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-assembly-plugin 78 | 2.5.1 79 | 80 | 81 | jar-with-dependencies 82 | package 83 | 84 | single 85 | 86 | 87 | 88 | 89 | 90 | assembly.xml 91 | 92 | false 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/src/main/java/org/hive2hive/android/deployment/MultiStableH2HPeers.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.android.deployment; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import net.tomp2p.relay.android.AndroidRelayServerConfig; 8 | import net.tomp2p.relay.buffer.MessageBufferConfiguration; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import com.typesafe.config.Config; 14 | import com.typesafe.config.ConfigFactory; 15 | 16 | /** 17 | * Creates multiple peers. The first one is the 'initial' peer, all others bootstrap to it. 18 | * This can be used to increase the number of seed nodes in the P2P network. 19 | * 20 | * @author Nico 21 | * 22 | */ 23 | public class MultiStableH2HPeers { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(StableH2HPeer.class); 26 | 27 | public static void main(String[] args) throws UnknownHostException { 28 | 29 | Config config = ConfigFactory.load("deployment.conf"); 30 | int numPeers = config.getInt("NumPeers"); 31 | int startPort = config.getInt("StartPort"); 32 | String externalAddressString = config.getString("ExternalAddress"); 33 | InetAddress externalAddress = null; 34 | if (!"auto".equalsIgnoreCase(externalAddressString)) { 35 | externalAddress = InetAddress.getByName(externalAddressString); 36 | } 37 | 38 | boolean acceptData = config.getBoolean("AcceptData"); 39 | boolean enableRelaying = config.getBoolean("Relay.enabled"); 40 | AndroidRelayServerConfig androidServer = null; 41 | if (enableRelaying) { 42 | String gcmKey = config.getString("Relay.GCM.api-key"); 43 | long bufferTimeout = config.getDuration("Relay.GCM.buffer-age-limit", TimeUnit.MILLISECONDS); 44 | MessageBufferConfiguration buffer = new MessageBufferConfiguration().bufferAgeLimit(bufferTimeout); 45 | androidServer = new AndroidRelayServerConfig(gcmKey, 5, buffer); 46 | } 47 | 48 | for (int i = 0; i < numPeers; i++) { 49 | InetAddress bootstrapAddress = null; 50 | if (i > 0) { 51 | if (externalAddress == null) { 52 | // bootstrap by default to 127.0.0.1 53 | bootstrapAddress = InetAddress.getLocalHost(); 54 | } else { 55 | // bootstrap to external address if existing 56 | bootstrapAddress = externalAddress; 57 | } 58 | } 59 | 60 | // iterate port to not reuse the same twice 61 | new StableH2HPeer(startPort + i, i != 0, bootstrapAddress, startPort, externalAddress, acceptData, 62 | enableRelaying, androidServer); 63 | } 64 | 65 | logger.debug("{} peers started!", numPeers); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/src/main/java/org/hive2hive/android/deployment/StableH2HPeer.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.android.deployment; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import net.tomp2p.connection.ConnectionBean; 8 | import net.tomp2p.nat.PeerBuilderNAT; 9 | import net.tomp2p.peers.PeerAddress; 10 | import net.tomp2p.relay.RelayType; 11 | import net.tomp2p.relay.android.AndroidRelayServerConfig; 12 | import net.tomp2p.relay.buffer.MessageBufferConfiguration; 13 | 14 | import org.hive2hive.core.api.H2HNode; 15 | import org.hive2hive.core.api.configs.FileConfiguration; 16 | import org.hive2hive.core.api.configs.NetworkConfiguration; 17 | import org.hive2hive.core.api.interfaces.IFileConfiguration; 18 | import org.hive2hive.core.api.interfaces.IH2HNode; 19 | import org.hive2hive.core.network.H2HStorageMemory; 20 | import org.hive2hive.core.network.H2HStorageMemory.StorageMemoryPutMode; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import com.typesafe.config.Config; 25 | import com.typesafe.config.ConfigFactory; 26 | 27 | /** 28 | * Creates a single stable peer with the configuration in the separate file 29 | * This can be used to either create a new P2P network or add a node to an existing network. 30 | * 31 | * @author Nico 32 | * 33 | */ 34 | public class StableH2HPeer { 35 | 36 | // File Configuration 37 | private static final IFileConfiguration fileConfig = FileConfiguration.createDefault(); 38 | 39 | private static final Logger logger = LoggerFactory.getLogger(StableH2HPeer.class); 40 | 41 | static { 42 | // increase timeouts 43 | ConnectionBean.DEFAULT_CONNECTION_TIMEOUT_TCP = 20000; 44 | ConnectionBean.DEFAULT_TCP_IDLE_SECONDS = 12; 45 | ConnectionBean.DEFAULT_UDP_IDLE_SECONDS = 12; 46 | } 47 | 48 | public static void main(String[] args) throws UnknownHostException { 49 | Config config = ConfigFactory.load("deployment-single.conf"); 50 | int port = config.getInt("Port"); 51 | boolean bootstrapEnabled = config.getBoolean("Bootstrap.enabled"); 52 | String inetString = config.getString("Bootstrap.address"); 53 | InetAddress bootstrapAddress = InetAddress.getByName(inetString); 54 | int bootstrapPort = config.getInt("Bootstrap.port"); 55 | 56 | InetAddress externalAddress = null; 57 | String externalAddressString = config.getString("ExternalAddress"); 58 | if (!"auto".equalsIgnoreCase(externalAddressString)) { 59 | externalAddress = InetAddress.getByName(externalAddressString); 60 | } 61 | 62 | boolean acceptData = config.getBoolean("AcceptData"); 63 | 64 | boolean enableRelaying = config.getBoolean("Relay.enabled"); 65 | AndroidRelayServerConfig androidServer = null; 66 | if (enableRelaying) { 67 | String gcmKey = config.getString("Relay.GCM.api-key"); 68 | long bufferTimeout = config.getDuration("Relay.GCM.buffer-age-limit", TimeUnit.MILLISECONDS); 69 | MessageBufferConfiguration buffer = new MessageBufferConfiguration().bufferAgeLimit(bufferTimeout); 70 | androidServer = new AndroidRelayServerConfig(gcmKey, 5, buffer); 71 | } 72 | 73 | new StableH2HPeer(port, bootstrapEnabled, bootstrapAddress, bootstrapPort, externalAddress, acceptData, 74 | enableRelaying, androidServer); 75 | } 76 | 77 | public StableH2HPeer(int port, boolean bootstrapEnabled, InetAddress bootstrapAddress, int bootstrapPort, 78 | InetAddress externalAddress, boolean acceptData, boolean enableRelaying, AndroidRelayServerConfig androidConfig) { 79 | 80 | IH2HNode node = H2HNode.createNode(fileConfig); 81 | NetworkConfiguration netConf = NetworkConfiguration.createInitial(); 82 | netConf.setPort(port); 83 | if (bootstrapEnabled) { 84 | netConf.setBootstrap(bootstrapAddress); 85 | netConf.setBootstrapPort(bootstrapPort); 86 | } 87 | 88 | if (!node.connect(netConf)) { 89 | logger.error("Peer cannot connect!"); 90 | return; 91 | } 92 | 93 | if (externalAddress != null) { 94 | logger.debug("Binding to address {}", externalAddress); 95 | PeerAddress peerAddress = node.getPeer().peerBean().serverPeerAddress().changeAddress(externalAddress); 96 | node.getPeer().peerBean().serverPeerAddress(peerAddress); 97 | } 98 | 99 | if (!acceptData) { 100 | logger.debug("Denying all data requests on this peer"); 101 | H2HStorageMemory storageLayer = (H2HStorageMemory) node.getPeer().storageLayer(); 102 | storageLayer.setPutMode(StorageMemoryPutMode.DENY_ALL); 103 | } 104 | 105 | // start relaying if required 106 | if (enableRelaying) { 107 | logger.debug("Starting relay functionality..."); 108 | 109 | PeerBuilderNAT nat = new PeerBuilderNAT(node.getPeer().peer()); 110 | if (androidConfig != null) { 111 | nat.addRelayServerConfiguration(RelayType.ANDROID, androidConfig); 112 | } 113 | nat.start(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/src/main/resources/deployment-single.conf: -------------------------------------------------------------------------------- 1 | ####################################################### 2 | # Configuration for the Hive2Hive Android Stable Peer # 3 | ####################################################### 4 | 5 | # Set the port to bind. On one machine, it is not possible to run multiple instances on the same port 6 | Port=4622 7 | 8 | # Either set the IP address, a host name or use "auto" in case the address should be determined automatically. 9 | # Note that the external IP address will not be detected automatically if this node is the initial. In this case, 10 | # you need to set it here manually. 11 | ExternalAddress = auto 12 | 13 | Bootstrap { 14 | # Enable bootstrapping if this peer is not the initial one. 15 | enabled = false 16 | # The address to connect to 17 | address = 127.0.0.1 18 | # The port to connect to 19 | port = 4622 20 | } 21 | 22 | # You can configure this peer to decline any put request or replication data 23 | AcceptData = true 24 | 25 | # Allows unreachable peers to connect to this node. This peer then forwards messages 26 | Relay { 27 | enabled = true 28 | 29 | # Enable GCM at some peers to serve as battery-saving relay peers 30 | GCM { 31 | api-key = "your-api-key" 32 | buffer-age-limit = 20s 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/src/main/resources/deployment.conf: -------------------------------------------------------------------------------- 1 | ####################################################### 2 | # Configuration for the Hive2Hive Android Stable Peer # 3 | ####################################################### 4 | 5 | # The number of peers to start (usually ~5-10) 6 | NumPeers = 5 7 | 8 | # The start port for the first peer 9 | StartPort=4622 10 | 11 | # Either set the IP address, a host name or use "auto" in case the address should be determined automatically. 12 | # Note that the external IP address will not be detected automatically if this node is the initial. In this case, 13 | # you need to set it here manually. 14 | ExternalAddress = auto 15 | 16 | # You can configure this peer to decline any put request or replication data 17 | AcceptData = true 18 | 19 | # Allows unreachable peers to connect to this node. This peer then forwards messages 20 | Relay { 21 | enabled = true 22 | 23 | # Enable GCM at some peers to serve as battery-saving relay peers 24 | GCM { 25 | api-key = "your-api-key" 26 | buffer-age-limit = 20s 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /org.hive2hive.android.deployment/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss} %-12.-12([%thread])[%-5level] %logger{0} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | ### Android template 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio files (IntelliJ) 31 | .idea/ 32 | 33 | # Apple files 34 | .DS_Store -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/.name: -------------------------------------------------------------------------------- 1 | org.hive2hive.mobile -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/dictionaries/nrutishauser.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rutishauser 5 | 6 | 7 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/apktool_lib_1_4_4_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/appcompat_v7_20_0_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/commons_io_2_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/core_1_51_0_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/el_api_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/fst_2_23.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/gcm_server_1_0_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/javassist_3_18_1_GA.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/json_simple_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/logback_android_classic_1_1_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/logback_android_core_1_1_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/mbassador_1_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/netty_buffer_4_0_25_Final.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/netty_common_4_0_25_Final.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/netty_transport_4_0_25_Final.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/objenesis_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/org_hive2hive_core_1_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/org_hive2hive_processframework_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/play_services_4_2_42.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/prov_1_51_0_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/slf4j_api_1_7_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/support_annotations_20_0_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/support_v4_20_0_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/tomp2p_android_5_0_Beta5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/tomp2p_core_5_0_Beta5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/tomp2p_dht_5_0_Beta5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/tomp2p_nat_5_0_Beta5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/libraries/tomp2p_replication_5_0_Beta5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'idea' 3 | 4 | android { 5 | compileSdkVersion 19 6 | buildToolsVersion '21.1.2' 7 | defaultConfig { 8 | applicationId 'org.hive2hive.mobile' 9 | minSdkVersion 14 10 | targetSdkVersion 19 11 | versionCode 3 12 | versionName '1.0.2' 13 | } 14 | 15 | dexOptions { 16 | // incremental true 17 | javaMaxHeapSize "4g" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled true 23 | zipAlignEnabled true 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | buildConfigField "String", "DEFAULT_BOOTSTRAP_ADDRESS", "\"bootstrap.example.com\"" 26 | buildConfigField "String", "H2H_VERSION", "\"1.2.2\"" 27 | buildConfigField "int", "DEFAULT_BOOTSTRAP_PORT", "4622" 28 | buildConfigField "int", "PEER_MAP_UPDATE_INTERVAL", "120" 29 | } 30 | debug { 31 | // minifyEnabled true 32 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | buildConfigField "String", "DEFAULT_BOOTSTRAP_ADDRESS", "\"192.168.1.30\"" 34 | buildConfigField "String", "H2H_VERSION", "\"1.2.2\"" 35 | buildConfigField "int", "DEFAULT_BOOTSTRAP_PORT", "4622" 36 | buildConfigField "int", "PEER_MAP_UPDATE_INTERVAL", "120" 37 | } 38 | } 39 | 40 | productFlavors { 41 | // Define separate dev and prod product flavors. 42 | dev { 43 | // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin 44 | // to pre-dex each module and produce an APK that can be tested on 45 | // Android Lollipop without time consuming dex merging processes. 46 | minSdkVersion 19 47 | } 48 | prod { 49 | // The actual minSdkVersion for the application. 50 | minSdkVersion 14 51 | } 52 | } 53 | 54 | packagingOptions { 55 | exclude 'META-INF/io.netty.versions.properties' 56 | exclude 'META-INF/INDEX.LIST' 57 | exclude 'logback.xml' 58 | } 59 | compileOptions { 60 | sourceCompatibility JavaVersion.VERSION_1_7 61 | targetCompatibility JavaVersion.VERSION_1_7 62 | } 63 | } 64 | 65 | repositories { 66 | maven { url 'http://tomp2p.net/dev/mvn/' } 67 | maven { url '// maven { url \'http://repo.hive2hive.org\' }' } 68 | mavenLocal() 69 | } 70 | 71 | dependencies { 72 | compile fileTree(dir: 'libs', include: ['*.jar']) 73 | compile ('net.tomp2p:tomp2p-android:5.0-Beta5') { 74 | exclude group: 'org.bitlet' 75 | } 76 | 77 | compile ('org.hive2hive:org.hive2hive.core:1.2.2') { 78 | exclude group: 'org.bouncycastle' 79 | } 80 | 81 | compile 'com.madgag.spongycastle:prov:1.51.0.0' 82 | compile 'com.github.tony19:logback-android-classic:1.1.1-3' 83 | compile 'com.google.android.gms:play-services:4.2.42' 84 | compile 'com.android.support:appcompat-v7:20.0.0' 85 | } 86 | 87 | idea { 88 | module { 89 | //if you love browsing Javadoc and sources 90 | downloadJavadoc = true 91 | downloadSources = true 92 | } 93 | } -------------------------------------------------------------------------------- /org.hive2hive.mobile/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 /Applications/Android Studio.app/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 javax.mail.** 20 | -dontwarn javax.swing.** 21 | -dontwarn javax.naming.** 22 | -dontwarn sun.misc.Unsafe 23 | -dontwarn sun.misc.Cleaner 24 | -dontwarn sun.nio.ch.FileChannelImpl 25 | -dontwarn sun.reflect.ReflectionFactory 26 | -dontwarn java.nio.channels.** 27 | -dontwarn org.apache.log4j.** 28 | -dontwarn ch.qos.logback.core.net.** 29 | -dontwarn org.apache.commons.logging.LogFactory 30 | -dontwarn com.sun.jdi.** 31 | -dontwarn java.beans.** 32 | -dontwarn java.awt.** 33 | -dontwarn java.applet.Applet 34 | -dontwarn java.net.ProtocolFamily 35 | -dontwarn java.net.StandardProtocolFamily 36 | -dontwarn net.tomp2p.holep.testapp.** 37 | -dontwarn org.bitlet.weupnp.** 38 | -dontwarn org.bouncycastle.** 39 | 40 | # Fix for netty 41 | -keepattributes Signature,InnerClasses 42 | -keepclasseswithmembers class io.netty.** { *; } 43 | -keepnames class sun.misc.Unsafe { *; } 44 | 45 | # For MBassador event bus 46 | -keepattributes *Annotation* 47 | -keep class javax.el.**,net.engio.**,android.support.annotation.** { *; } 48 | -keep class org.hive2hive.mobile.files.AndroidFileEventListener { *; } 49 | -keep class org.hive2hive.core.events.** { *; } 50 | 51 | # For FST serializer and encryption 52 | -keep class org.nustaq.**,javassist.**,org.objenesis.** { *; } 53 | -keep class org.spongycastle.** { *; } 54 | -keepclasseswithmembers class org.spongycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey { *; } 55 | -keepclasseswithmembers class org.spongycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey { *; } 56 | -keepclasseswithmembers class org.spongycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey { *; } 57 | 58 | #-keepclasseswithmembernames class org.hive2hive.core.**,net.tomp2p.** { *; } 59 | 60 | # Keep compatibility for serialized classes 61 | -keepnames class * implements java.io.Serializable 62 | -keepclassmembers class * implements java.io.Serializable { 63 | static final long serialVersionUID; 64 | private static final java.io.ObjectStreamField[] serialPersistentFields; 65 | !static !transient ; 66 | private void writeObject(java.io.ObjectOutputStream); 67 | private void readObject(java.io.ObjectInputStream); 68 | java.lang.Object writeReplace(); 69 | java.lang.Object readResolve(); 70 | } 71 | 72 | # For debug 73 | #-keep class ch.qos.** { *; } 74 | #-keep class org.slf4j.** { *; } -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/androidTest/java/org/hive2hive/mobile/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 34 | 35 | 36 | 37 | 38 | 39 | 46 | 49 | 50 | 55 | 56 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/assets/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/H2HApplication.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import net.tomp2p.connection.ConnectionBean; 7 | import net.tomp2p.dht.PeerDHT; 8 | import net.tomp2p.relay.buffer.BufferRequestListener; 9 | 10 | import org.hive2hive.core.H2HConstants; 11 | import org.hive2hive.core.api.interfaces.IH2HNode; 12 | import org.hive2hive.core.api.interfaces.INetworkConfiguration; 13 | import org.hive2hive.core.network.IPeerHolder; 14 | import org.hive2hive.mobile.common.ApplicationHelper; 15 | import org.hive2hive.mobile.common.ConnectionMode; 16 | import org.hive2hive.mobile.common.RelayMode; 17 | import org.hive2hive.mobile.files.AndroidFile; 18 | 19 | /** 20 | * To be able to configure some things when the application starts / ends / ... 21 | */ 22 | public class H2HApplication extends Application implements IPeerHolder { 23 | 24 | private BufferRequestListener bufferListener; 25 | private IH2HNode h2hNode; 26 | private RelayMode relayMode; 27 | private INetworkConfiguration networkConfig; 28 | private AndroidFile treeRoot; 29 | private ConnectionMode lastMode; 30 | private String userId; 31 | 32 | @Override 33 | public void onCreate() { 34 | super.onCreate(); 35 | 36 | // Prevent using IPv6, prefer IPv4 37 | System.setProperty("java.net.preferIPv4Stack", "true"); 38 | 39 | lastMode = ApplicationHelper.getConnectionMode(this); 40 | 41 | // ConnectionChangeListener connectionChangeListener = new ConnectionChangeListener(this); 42 | // IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"); 43 | // registerReceiver(connectionChangeListener, filter); 44 | 45 | // increase timeouts 46 | ConnectionBean.DEFAULT_CONNECTION_TIMEOUT_TCP = 20000; 47 | ConnectionBean.DEFAULT_TCP_IDLE_SECONDS = 12; 48 | ConnectionBean.DEFAULT_UDP_IDLE_SECONDS = 12; 49 | } 50 | 51 | @Override 52 | protected void attachBaseContext(Context base) { 53 | super.attachBaseContext(base); 54 | // enable multidex 55 | // MultiDex.install(this); 56 | } 57 | 58 | /** 59 | * Singleton-like application data that needs to be present in the whole application. 60 | * Note that the content of this data is removed once the process is killed. 61 | */ 62 | 63 | public void relayMode(RelayMode relayMode) { 64 | this.relayMode = relayMode; 65 | } 66 | 67 | public RelayMode relayMode() { 68 | return relayMode; 69 | } 70 | 71 | public void bufferListener(BufferRequestListener bufferListener) { 72 | this.bufferListener = bufferListener; 73 | } 74 | 75 | public BufferRequestListener bufferListener() { 76 | return bufferListener; 77 | } 78 | 79 | public void h2hNode(IH2HNode h2hNode) { 80 | this.h2hNode = h2hNode; 81 | } 82 | 83 | public IH2HNode h2hNode() { 84 | return h2hNode; 85 | } 86 | 87 | public void networkConfig(INetworkConfiguration networkConfig) { 88 | this.networkConfig = networkConfig; 89 | } 90 | 91 | public INetworkConfiguration networkConfig() { 92 | return networkConfig; 93 | } 94 | 95 | 96 | public void logout() { 97 | treeRoot = null; 98 | userId = null; 99 | } 100 | 101 | /** 102 | * Holds the latest file list of the currently logged in user 103 | * 104 | * @param treeRoot 105 | */ 106 | public void currentTree(AndroidFile treeRoot) { 107 | this.treeRoot = treeRoot; 108 | } 109 | 110 | /** 111 | * @return the last file taste list or null if not fetched yet 112 | */ 113 | public AndroidFile currentTree() { 114 | return treeRoot; 115 | } 116 | 117 | public void currentUser(String userId) { 118 | this.userId = userId; 119 | } 120 | 121 | public String currentUser() { 122 | return userId; 123 | } 124 | 125 | @Override 126 | public PeerDHT getPeer() { 127 | if (h2hNode == null) { 128 | return null; 129 | } 130 | return h2hNode.getPeer(); 131 | } 132 | 133 | /** 134 | * @return the last connection mode 135 | */ 136 | public ConnectionMode lastMode() { 137 | return lastMode; 138 | } 139 | 140 | /** 141 | * Set the last mode in order to detect changes when the {@link org.hive2hive.mobile.connection.ConnectionChangeListener} is triggered 142 | */ 143 | public void lastMode(ConnectionMode lastMode) { 144 | this.lastMode = lastMode; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/AndroidFileAgent.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | 6 | import org.apache.commons.io.FileUtils; 7 | import org.hive2hive.core.file.IFileAgent; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | /** 15 | * Files are located at a public location, whereas cache is stored internally 16 | * Created by Nico Rutishauser on 24.11.14. 17 | */ 18 | public class AndroidFileAgent implements IFileAgent { 19 | 20 | private static final Logger LOG = LoggerFactory.getLogger(AndroidFileAgent.class); 21 | 22 | private final File rootDir; 23 | private final File cacheDir; 24 | 25 | public AndroidFileAgent(Context context, String username) { 26 | cacheDir = new File(context.getCacheDir(), username); 27 | rootDir = getStorageLocation(context, username); 28 | if (!rootDir.exists() && !rootDir.mkdirs()) { 29 | LOG.error("Root directory not created"); 30 | } 31 | } 32 | 33 | public static File getStorageLocation(Context context, String username) { 34 | File folder; 35 | if (ApplicationHelper.isExternalStorageWritable()) { 36 | // Get the directory for the user's public pictures directory. 37 | folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "Hive2Hive"); 38 | } else { 39 | LOG.info("Need to save logs to internal storage because external is not available"); 40 | folder = new File(context.getFilesDir(), "Hive2Hive"); 41 | } 42 | 43 | if (username == null) { 44 | return folder; 45 | } else { 46 | return new File(folder, username); 47 | } 48 | } 49 | 50 | @Override 51 | public File getRoot() { 52 | return rootDir; 53 | } 54 | 55 | @Override 56 | public void writeCache(String key, byte[] data) throws IOException { 57 | File file = new File(cacheDir, key); 58 | FileUtils.writeByteArrayToFile(file, data); 59 | } 60 | 61 | @Override 62 | public byte[] readCache(String key) throws IOException { 63 | File file = new File(cacheDir, key); 64 | return FileUtils.readFileToByteArray(file); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/ApplicationHelper.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.net.ConnectivityManager; 7 | import android.net.NetworkInfo; 8 | import android.net.Uri; 9 | import android.os.Environment; 10 | import android.os.Process; 11 | import android.provider.OpenableColumns; 12 | 13 | import com.google.android.gms.common.ConnectionResult; 14 | import com.google.android.gms.common.GooglePlayServicesUtil; 15 | 16 | import org.apache.http.HttpEntity; 17 | import org.apache.http.HttpResponse; 18 | import org.apache.http.client.HttpClient; 19 | import org.apache.http.client.methods.HttpGet; 20 | import org.apache.http.impl.client.DefaultHttpClient; 21 | import org.apache.http.util.EntityUtils; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.net.Inet4Address; 26 | import java.net.InetAddress; 27 | import java.net.NetworkInterface; 28 | import java.net.SocketException; 29 | import java.util.Enumeration; 30 | 31 | /** 32 | * Created by Nico Rutishauser on 08.09.14. 33 | */ 34 | public class ApplicationHelper { 35 | 36 | private static final Logger LOG = LoggerFactory.getLogger(ApplicationHelper.class); 37 | private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; 38 | private static final String EXTERNAL_IP_LOOKUP_URL = "http://wtfismyip.com/text"; 39 | 40 | /** 41 | * Kills the application immediately. Only use in emergency! 42 | */ 43 | public static void killApplication() { 44 | android.os.Process.killProcess(Process.myPid()); 45 | } 46 | 47 | /** 48 | * Check the device to make sure it has the Google Play Services APK. If 49 | * it doesn't, display a dialog that allows users to download the APK from 50 | * the Google Play Store or enable it in the device's system settings. 51 | */ 52 | public static boolean checkPlayServices(Activity activity) { 53 | int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity); 54 | if (resultCode != ConnectionResult.SUCCESS) { 55 | if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { 56 | GooglePlayServicesUtil.getErrorDialog(resultCode, activity, 57 | PLAY_SERVICES_RESOLUTION_REQUEST).show(); 58 | } else { 59 | LOG.info("This device is not supported."); 60 | } 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | /* Checks if external storage is available for read and write */ 67 | public static boolean isExternalStorageWritable() { 68 | String state = Environment.getExternalStorageState(); 69 | if (Environment.MEDIA_MOUNTED.equals(state)) { 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | /** 76 | * Maps a URI starting with 'content:' to a real file name 77 | */ 78 | public static String getFileNameContentURI(Context context, Uri contentUri) { 79 | Cursor returnCursor = 80 | context.getContentResolver().query(contentUri, null, null, null, null); 81 | int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); 82 | returnCursor.moveToFirst(); 83 | return returnCursor.getString(nameIndex); 84 | } 85 | 86 | /** 87 | * @return the current connection mode 88 | */ 89 | public static ConnectionMode getConnectionMode(Context context) { 90 | ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 91 | NetworkInfo wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 92 | if (wifiInfo.isConnected()) { 93 | return ConnectionMode.WIFI; 94 | } 95 | NetworkInfo mobileInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 96 | if (mobileInfo.isConnected()) { 97 | return ConnectionMode.CELLULAR; 98 | } 99 | 100 | return ConnectionMode.OFFLINE; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/BaseProgressTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | import android.app.ProgressDialog; 4 | import android.os.AsyncTask; 5 | 6 | import org.hive2hive.mobile.H2HApplication; 7 | 8 | /** 9 | * @author Nico 10 | */ 11 | public abstract class BaseProgressTask extends AsyncTask { 12 | 13 | protected final H2HApplication context; 14 | private final ISuccessFailListener listener; 15 | private final ProgressDialog progressDialog; 16 | 17 | private String[] progressMessages; 18 | 19 | public BaseProgressTask(H2HApplication context, ISuccessFailListener listener, ProgressDialog progressDialog) { 20 | this.context = context; 21 | this.listener = listener; 22 | this.progressDialog = progressDialog; 23 | } 24 | 25 | @Override 26 | protected void onPreExecute() { 27 | progressMessages = getProgressMessages(); 28 | 29 | progressDialog.setProgress(0); 30 | progressDialog.setMax(progressMessages.length); 31 | progressDialog.setIndeterminate(false); 32 | 33 | progressDialog.setMessage(progressMessages[0]); 34 | progressDialog.show(); 35 | } 36 | 37 | protected abstract String[] getProgressMessages(); 38 | 39 | @Override 40 | protected final void onProgressUpdate(Integer... values) { 41 | super.onProgressUpdate(values); 42 | if (values != null && values.length > 0) { 43 | if (values[0] < progressMessages.length) { 44 | progressDialog.setMessage(progressMessages[values[0]]); 45 | } 46 | progressDialog.setProgress(values[0]); 47 | } 48 | } 49 | 50 | @Override 51 | protected void onPostExecute(Boolean success) { 52 | if (success) { 53 | listener.onSuccess(); 54 | } else { 55 | listener.onFail(); 56 | } 57 | 58 | // close the progress dialog in any case 59 | progressDialog.dismiss(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/ConnectionMode.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | /** 4 | * @author Nico 5 | */ 6 | public enum ConnectionMode { 7 | WIFI("Wifi"), 8 | CELLULAR("3G"), 9 | OFFLINE("Offline"); 10 | 11 | private final String name; 12 | 13 | private ConnectionMode(String name) { 14 | this.name = name; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/ISuccessFailListener.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | /** 4 | * Created by Nico Rutishauser on 24.08.14. 5 | */ 6 | public interface ISuccessFailListener { 7 | 8 | void onSuccess(); 9 | 10 | void onFail(); 11 | } 12 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/RelayMode.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | /** 4 | * Created by Nico Rutishauser 5 | */ 6 | public enum RelayMode { 7 | GCM(0), 8 | TCP(1), 9 | FULL(2); 10 | 11 | private final int spinnerPosition; 12 | 13 | private RelayMode(int spinnerPosition) { 14 | this.spinnerPosition = spinnerPosition; 15 | } 16 | 17 | public int spinnerPosition() { 18 | return spinnerPosition; 19 | } 20 | 21 | public static RelayMode getByPosition(int pos) { 22 | for (RelayMode mode : RelayMode.values()) { 23 | if (mode.spinnerPosition() == pos) { 24 | return mode; 25 | } 26 | } 27 | 28 | // by default 29 | return GCM; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/common/UserPermissionComparator.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.common; 2 | 3 | import org.hive2hive.core.model.PermissionType; 4 | import org.hive2hive.core.model.UserPermission; 5 | 6 | import java.util.Comparator; 7 | 8 | /** 9 | * @author Nico 10 | */ 11 | public class UserPermissionComparator implements Comparator { 12 | 13 | @Override 14 | public int compare(UserPermission perm1, UserPermission perm2) { 15 | if (perm1.getPermission() == perm2.getPermission()) { 16 | return perm1.getUserId().toLowerCase().compareTo(perm2.getUserId().toLowerCase()); 17 | } else { 18 | return perm1.getPermission() == PermissionType.WRITE ? -1 : 1; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/connection/ConnectionChangeListener.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.connection; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import org.hive2hive.mobile.H2HApplication; 8 | import org.hive2hive.mobile.common.ApplicationHelper; 9 | import org.hive2hive.mobile.common.ConnectionMode; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author Nico 15 | */ 16 | public class ConnectionChangeListener extends BroadcastReceiver { 17 | 18 | private static final Logger LOG = LoggerFactory.getLogger(ConnectionChangeListener.class); 19 | private final H2HApplication application; 20 | 21 | public ConnectionChangeListener(H2HApplication application) { 22 | this.application = application; 23 | } 24 | 25 | @Override 26 | public void onReceive(Context context, Intent intent) { 27 | ConnectionMode mode = ApplicationHelper.getConnectionMode(context); 28 | if (application.lastMode() == mode) { 29 | LOG.trace("Mode did not change, is still {}", mode); 30 | return; 31 | } else if (application.h2hNode() == null || !application.h2hNode().isConnected()) { 32 | LOG.trace("Connectivity mode did change to {} but node is not connected", mode); 33 | return; 34 | } 35 | 36 | LOG.debug("Network connection changed to {}", mode); 37 | 38 | // TODO take actions (reconnect node) 39 | 40 | application.lastMode(mode); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/connection/ConnectionSetupTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.connection; 2 | 3 | import android.app.ProgressDialog; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | import android.provider.Settings; 7 | 8 | import net.tomp2p.futures.FutureBootstrap; 9 | import net.tomp2p.nat.FutureRelayNAT; 10 | import net.tomp2p.nat.PeerBuilderNAT; 11 | import net.tomp2p.nat.PeerNAT; 12 | import net.tomp2p.peers.PeerAddress; 13 | import net.tomp2p.relay.RelayClientConfig; 14 | import net.tomp2p.relay.android.AndroidRelayClientConfig; 15 | import net.tomp2p.relay.buffer.BufferRequestListener; 16 | import net.tomp2p.relay.tcp.TCPRelayClientConfig; 17 | 18 | import org.hive2hive.core.H2HConstants; 19 | import org.hive2hive.core.api.H2HNode; 20 | import org.hive2hive.core.api.configs.FileConfiguration; 21 | import org.hive2hive.core.api.configs.NetworkConfiguration; 22 | import org.hive2hive.core.api.interfaces.IH2HNode; 23 | import org.hive2hive.core.serializer.FSTSerializer; 24 | import org.hive2hive.core.serializer.IH2HSerialize; 25 | import org.hive2hive.mobile.BuildConfig; 26 | import org.hive2hive.mobile.H2HApplication; 27 | import org.hive2hive.mobile.R; 28 | import org.hive2hive.mobile.common.ApplicationHelper; 29 | import org.hive2hive.mobile.common.BaseProgressTask; 30 | import org.hive2hive.mobile.common.ISuccessFailListener; 31 | import org.hive2hive.mobile.common.RelayMode; 32 | import org.hive2hive.mobile.gcm.GCMRegistrationUtil; 33 | import org.hive2hive.mobile.security.SCSecurityClassProvider; 34 | import org.hive2hive.mobile.security.SpongyCastleEncryption; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | import java.net.InetAddress; 39 | import java.net.UnknownHostException; 40 | import java.util.Collection; 41 | import java.util.UUID; 42 | 43 | /** 44 | * @author Nico 45 | */ 46 | public class ConnectionSetupTask extends BaseProgressTask { 47 | 48 | private static final Logger LOG = LoggerFactory.getLogger(ConnectionSetupTask.class); 49 | 50 | private final String bootstrapAddressString; 51 | private final int bootstrapPort; 52 | private final long gcmSenderId; 53 | 54 | private final SharedPreferences prefs; 55 | 56 | private String registrationId; 57 | /** 58 | * variables that will be stored in the context 59 | */ 60 | private final RelayMode relayMode; 61 | private NetworkConfiguration networkConfig; 62 | private BufferRequestListener bufferListener; 63 | private IH2HNode h2hNode; 64 | private Collection boostrapAddresses; 65 | 66 | public ConnectionSetupTask(String bootstrapAddress, int bootstrapPort, RelayMode relayMode, long gcmSenderId, H2HApplication context, ISuccessFailListener listener, ProgressDialog progressDialog) { 67 | super(context, listener, progressDialog); 68 | this.bootstrapAddressString = bootstrapAddress; 69 | this.bootstrapPort = bootstrapPort; 70 | this.relayMode = relayMode; 71 | this.gcmSenderId = gcmSenderId; 72 | this.prefs = PreferenceManager.getDefaultSharedPreferences(context); 73 | } 74 | 75 | @Override 76 | protected String[] getProgressMessages() { 77 | String[] progressMessages = new String[4]; 78 | progressMessages[0] = context.getString(R.string.progress_connect_resolve_address); 79 | progressMessages[1] = context.getString(R.string.progress_connect_bootstrapping); 80 | progressMessages[2] = context.getString(R.string.progress_connect_register_gcm); 81 | progressMessages[3] = context.getString(R.string.progress_connect_relay); 82 | return progressMessages; 83 | } 84 | 85 | @Override 86 | protected Boolean doInBackground(Void... voids) { 87 | publishProgress(0); 88 | try { 89 | InetAddress bootstrapAddress = InetAddress.getByName(bootstrapAddressString); 90 | String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); 91 | if (deviceId == null || deviceId.isEmpty()) { 92 | deviceId = UUID.randomUUID().toString(); 93 | } 94 | LOG.debug("Using node ID: {}", deviceId); 95 | 96 | int bindPort = prefs.getInt(context.getString(R.string.pref_port_key), H2HConstants.H2H_PORT); 97 | LOG.debug("Binding port {}", bindPort); 98 | 99 | networkConfig = NetworkConfiguration.create(deviceId, bootstrapAddress, bootstrapPort).setPort(bindPort); 100 | } catch (UnknownHostException e) { 101 | LOG.error("Cannot resolve host {}", bootstrapAddressString, e); 102 | return false; 103 | } 104 | 105 | publishProgress(1); 106 | if (!createPeer()) { 107 | return false; 108 | } 109 | 110 | publishProgress(2); 111 | if (!registerGCM()) { 112 | return false; 113 | } 114 | 115 | publishProgress(3); 116 | if (!connectNAT()) { 117 | return false; 118 | } 119 | 120 | // store the peerNAT in the application context 121 | context.h2hNode(h2hNode); 122 | context.networkConfig(networkConfig); 123 | context.bufferListener(bufferListener); 124 | context.relayMode(relayMode); 125 | 126 | LOG.debug("Peer setup finished successfully"); 127 | publishProgress(4); 128 | return true; 129 | } 130 | 131 | /** 132 | * Creates a peer and returns true if successful 133 | */ 134 | private boolean createPeer() { 135 | IH2HSerialize serializer = new FSTSerializer(false, new SCSecurityClassProvider()); 136 | h2hNode = H2HNode.createNode(FileConfiguration.createDefault(), new SpongyCastleEncryption(serializer), serializer); 137 | if (h2hNode.connect(networkConfig)) { 138 | LOG.debug("H2HNode successfully created."); 139 | 140 | FutureBootstrap bootstrap = h2hNode.getPeer().peer().bootstrap().inetAddress(networkConfig.getBootstrapAddress()).ports(networkConfig.getBootstrapPort()).start(); 141 | bootstrap.awaitUninterruptibly(); 142 | boostrapAddresses = bootstrap.bootstrapTo(); 143 | return true; 144 | } else { 145 | LOG.error("H2HNode cannot connect."); 146 | return false; 147 | } 148 | 149 | 150 | } 151 | 152 | /** 153 | * Register Google Cloud Messaging. Returns true if successful 154 | */ 155 | private boolean registerGCM() { 156 | if (relayMode == RelayMode.GCM) { 157 | registrationId = GCMRegistrationUtil.getRegistrationId(context, gcmSenderId); 158 | return registrationId != null; 159 | } else { 160 | // don't require a GCM registration id because not necessary 161 | return true; 162 | } 163 | } 164 | 165 | /** 166 | * Connects to the relay node and returns true if successful 167 | */ 168 | private boolean connectNAT() { 169 | int mapUpdateInterval = BuildConfig.PEER_MAP_UPDATE_INTERVAL; 170 | String mapUpdateIntervalString = prefs.getString(context.getString(R.string.pref_map_update_interval_key), String.valueOf(mapUpdateInterval)); 171 | try { 172 | mapUpdateInterval = Integer.valueOf(mapUpdateIntervalString); 173 | LOG.debug("Use configured map update interval of {}s", mapUpdateInterval); 174 | } catch (NumberFormatException e) { 175 | LOG.warn("Cannot parse the invalid map update interval string '{}'. Use default value {}s", mapUpdateIntervalString, mapUpdateInterval); 176 | } 177 | 178 | RelayClientConfig config; 179 | switch (relayMode) { 180 | case FULL: 181 | // don't set up any NAT 182 | LOG.warn("Don't use relay functionality. Make sure to not be behind a firewall!"); 183 | return true; 184 | case GCM: 185 | config = new AndroidRelayClientConfig(registrationId, mapUpdateInterval).manualRelays(boostrapAddresses); 186 | break; 187 | case TCP: 188 | config = new TCPRelayClientConfig().manualRelays(boostrapAddresses).peerMapUpdateInterval(mapUpdateInterval); 189 | break; 190 | default: 191 | LOG.debug("Invalid relay mode {}", relayMode); 192 | return false; 193 | } 194 | LOG.debug("Use {} as relay type. Map updateView interval is {}s", relayMode, mapUpdateInterval); 195 | 196 | PeerNAT peerNat = new PeerBuilderNAT(h2hNode.getPeer().peer()).start(); 197 | FutureRelayNAT futureRelayNAT = peerNat.startRelay(config, boostrapAddresses.iterator().next()).awaitUninterruptibly(); 198 | if (futureRelayNAT.isSuccess()) { 199 | LOG.debug("Successfully connected to Relay."); 200 | bufferListener = futureRelayNAT.bufferRequestListener(); 201 | return true; 202 | } else { 203 | LOG.error("Cannot connect to Relay. Reason: {}", futureRelayNAT.failedReason()); 204 | return false; 205 | } 206 | } 207 | 208 | @Override 209 | protected void onPostExecute(Boolean success) { 210 | if (!success && h2hNode != null) { 211 | h2hNode.disconnect(); 212 | } 213 | super.onPostExecute(success); 214 | } 215 | 216 | @Override 217 | protected void onCancelled(Boolean aBoolean) { 218 | if (h2hNode != null) { 219 | h2hNode.disconnect(); 220 | } 221 | 222 | super.onCancelled(aBoolean); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/connection/DisconnectTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.connection; 2 | 3 | import android.app.ProgressDialog; 4 | 5 | import org.hive2hive.core.api.interfaces.IH2HNode; 6 | import org.hive2hive.mobile.H2HApplication; 7 | import org.hive2hive.mobile.R; 8 | import org.hive2hive.mobile.common.BaseProgressTask; 9 | import org.hive2hive.mobile.common.ISuccessFailListener; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author Nico 15 | */ 16 | public class DisconnectTask extends BaseProgressTask { 17 | 18 | private static final Logger LOG = LoggerFactory.getLogger(DisconnectTask.class); 19 | 20 | public DisconnectTask(H2HApplication context, ISuccessFailListener listener, ProgressDialog progressDialog) { 21 | super(context, listener, progressDialog); 22 | } 23 | 24 | @Override 25 | protected String[] getProgressMessages() { 26 | return new String[]{context.getString(R.string.progress_disconnect_shutdown)}; 27 | } 28 | 29 | @Override 30 | protected Boolean doInBackground(Void... voids) { 31 | IH2HNode node = context.h2hNode(); 32 | if (node != null && node.isConnected()) { 33 | LOG.debug("Start disconnecting the peer..."); 34 | boolean shutdown = context.h2hNode().disconnect(); 35 | 36 | if (shutdown) { 37 | LOG.debug("Successfully shut down the peer."); 38 | context.logout(); 39 | context.h2hNode(null); 40 | } else { 41 | LOG.warn("Could not disconnect properly"); 42 | } 43 | 44 | return shutdown; 45 | } else { 46 | // no need to disconnect 47 | return true; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/AndroidFile.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files; 2 | 3 | import android.widget.ImageView; 4 | import android.widget.ProgressBar; 5 | import android.widget.TextView; 6 | 7 | import org.hive2hive.core.model.PermissionType; 8 | import org.hive2hive.core.model.UserPermission; 9 | import org.hive2hive.core.processes.files.list.FileNode; 10 | import org.hive2hive.core.security.HashUtil; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.util.Set; 17 | import java.util.TreeSet; 18 | 19 | /** 20 | * @author Nico 21 | */ 22 | public class AndroidFile implements Comparable { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(AndroidFile.class); 25 | 26 | private final boolean isFile; 27 | private final File file; 28 | private final String path; 29 | private final Set children = new TreeSet<>(); 30 | private final Set permissions; 31 | private AndroidFile parent; 32 | private FileState state; 33 | 34 | private TextView detailsView; 35 | private ImageView imageView; 36 | private ProgressBar progressBar; 37 | 38 | /** 39 | * Recursively copy the tree from the @link FileNode} 40 | */ 41 | public AndroidFile(FileNode node, AndroidFile parent) { 42 | this.file = node.getFile(); 43 | this.isFile = node.isFile(); 44 | this.path = node.getPath(); 45 | this.parent = parent; 46 | this.permissions = node.getUserPermissions(); 47 | 48 | if (node.isFolder()) { 49 | if (node.isShared() && parent != null) { 50 | if (parent.isShared()) { 51 | this.state = FileState.SHARED; 52 | } else { 53 | this.state = FileState.TOP_SHARED; 54 | } 55 | } else { 56 | this.state = FileState.IN_SYNC; 57 | } 58 | for (FileNode child : node.getChildren()) { 59 | children.add(new AndroidFile(child, this)); 60 | } 61 | } else if (node.getFile().exists()) { 62 | try { 63 | // check if hash of file same as the hash of the item 64 | if (HashUtil.compare(node.getFile(), node.getMd5())) { 65 | this.state = FileState.IN_SYNC; 66 | } else { 67 | this.state = FileState.OUTDATED; 68 | } 69 | } catch (IOException e) { 70 | LOG.warn("Cannot compare the hash of the item {} with the hash in the user profile", node, e); 71 | this.state = FileState.OUTDATED; 72 | } 73 | } else { 74 | this.state = FileState.ON_AIR; 75 | } 76 | } 77 | 78 | /** 79 | * Default constructor. Add children separately 80 | */ 81 | public AndroidFile(File file, boolean isFile, AndroidFile parent, FileState state, Set permissions) { 82 | this.file = file; 83 | this.isFile = isFile; 84 | this.parent = parent; 85 | this.permissions = permissions; 86 | 87 | if (isFile) { 88 | this.path = parent.getPath() + file.getName(); 89 | } else { 90 | this.path = parent.getPath() + file.getName() + File.separator; 91 | } 92 | this.state = state; 93 | } 94 | 95 | public boolean isRoot() { 96 | return parent == null; 97 | } 98 | 99 | public boolean isFile() { 100 | return isFile; 101 | } 102 | 103 | public boolean isFolder() { 104 | return !isFile(); 105 | } 106 | 107 | public File getFile() { 108 | return file; 109 | } 110 | 111 | public Set getPermissions() { 112 | return permissions; 113 | } 114 | 115 | public boolean canWrite(String userId) { 116 | for (UserPermission permission : permissions) { 117 | if (permission.getPermission() == PermissionType.WRITE && permission.getUserId().equalsIgnoreCase(userId)) { 118 | return true; 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | public boolean isShared() { 125 | return permissions.size() > 1; 126 | } 127 | 128 | public String getPath() { 129 | return path; 130 | } 131 | 132 | public FileState getState() { 133 | return state; 134 | } 135 | 136 | public void setState(FileState state) { 137 | this.state = state; 138 | } 139 | 140 | public void setState(FileState state, FileArrayAdapter adapter) { 141 | this.state = state; 142 | if (imageView != null) { 143 | imageView.setImageDrawable(adapter.getContext().getResources().getDrawable(state.getIconId(isFile()))); 144 | } 145 | if (detailsView != null) { 146 | detailsView.setText(state.getMessageId()); 147 | } 148 | 149 | adapter.updateView(false); 150 | } 151 | 152 | public Set getChildren() { 153 | return children; 154 | } 155 | 156 | public AndroidFile getParent() { 157 | return parent; 158 | } 159 | 160 | public void setParent(AndroidFile parent) { 161 | this.parent = parent; 162 | } 163 | 164 | public void setDetailsView(TextView detailsView) { 165 | this.detailsView = detailsView; 166 | } 167 | 168 | public void setImageView(ImageView imageView) { 169 | this.imageView = imageView; 170 | } 171 | 172 | public void setProgressBar(ProgressBar progressBar) { 173 | this.progressBar = progressBar; 174 | } 175 | 176 | public ProgressBar getProgressBar() { 177 | return progressBar; 178 | } 179 | 180 | public AndroidFile findByFile(File file) { 181 | if (this.file.equals(file)) { 182 | // can stop the search, we found it here 183 | return this; 184 | } 185 | 186 | for (AndroidFile child : children) { 187 | AndroidFile found = child.findByFile(file); 188 | if (found != null) { 189 | return found; 190 | } 191 | } 192 | 193 | // no child matches 194 | return null; 195 | } 196 | 197 | @Override 198 | public int hashCode() { 199 | return getPath().hashCode(); 200 | } 201 | 202 | @Override 203 | public boolean equals(Object o) { 204 | if (o == null) { 205 | return false; 206 | } else if (o instanceof AndroidFile) { 207 | AndroidFile other = ((AndroidFile) o); 208 | return other.isFile == isFile() && other.getPath().equalsIgnoreCase(getPath()); 209 | } else { 210 | return false; 211 | } 212 | } 213 | 214 | @Override 215 | public int compareTo(AndroidFile other) { 216 | if (other == null) { 217 | return 1; 218 | } else if (other.isFile() != isFile()) { 219 | // folders first 220 | return isFolder() ? -1 : 1; 221 | } else { 222 | // same type than other, order by default file 223 | return file.getName().toLowerCase().compareTo(other.getFile().getName().toLowerCase()); 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/AndroidFileComparator.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files; 2 | 3 | import java.util.Comparator; 4 | 5 | /** 6 | * @author Nico 7 | */ 8 | public class AndroidFileComparator implements Comparator { 9 | 10 | @Override 11 | public int compare(AndroidFile file1, AndroidFile file2) { 12 | return file1.compareTo(file2); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/AndroidFileEventListener.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files; 2 | 3 | import net.engio.mbassy.listener.Handler; 4 | import net.engio.mbassy.listener.Listener; 5 | import net.engio.mbassy.listener.References; 6 | 7 | import org.hive2hive.core.events.framework.interfaces.IFileEventListener; 8 | import org.hive2hive.core.events.framework.interfaces.file.IFileAddEvent; 9 | import org.hive2hive.core.events.framework.interfaces.file.IFileDeleteEvent; 10 | import org.hive2hive.core.events.framework.interfaces.file.IFileMoveEvent; 11 | import org.hive2hive.core.events.framework.interfaces.file.IFileShareEvent; 12 | import org.hive2hive.core.events.framework.interfaces.file.IFileUpdateEvent; 13 | import org.hive2hive.core.model.UserPermission; 14 | import org.hive2hive.mobile.H2HApplication; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.io.File; 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.Map; 22 | import java.util.Set; 23 | 24 | /** 25 | * @author Nico 26 | */ 27 | @Listener(references = References.Strong) 28 | public class AndroidFileEventListener implements IFileEventListener { 29 | 30 | private static final Logger LOG = LoggerFactory.getLogger(AndroidFileEventListener.class); 31 | private final FilesActivity activity; 32 | private final H2HApplication context; 33 | 34 | // used to temporarily mark shared folders correctly 35 | private final Map> currentShareInvitations = new HashMap<>(1); 36 | 37 | public AndroidFileEventListener(FilesActivity activity, H2HApplication context) { 38 | this.activity = activity; 39 | this.context = context; 40 | } 41 | 42 | private FilesFragment getFragment() { 43 | return activity.getCurrentFragment(); 44 | } 45 | 46 | private AndroidFile createAndroidFile(File file, boolean isFile, AndroidFile parent) { 47 | FileState state; 48 | Set permissions; 49 | if (isFile) { 50 | state = FileState.ON_AIR; 51 | permissions = new HashSet<>(parent.getPermissions()); 52 | } else if (currentShareInvitations.containsKey(file)) { 53 | permissions = currentShareInvitations.remove(file); 54 | state = FileState.SHARED; 55 | } else { 56 | state = FileState.IN_SYNC; 57 | permissions = new HashSet<>(parent.getPermissions()); 58 | } 59 | 60 | return new AndroidFile(file, isFile, parent, state, permissions); 61 | } 62 | 63 | @Override 64 | @Handler 65 | public void onFileAdd(IFileAddEvent fileEvent) { 66 | LOG.debug("Remotely added file {}", fileEvent.getFile()); 67 | final FilesFragment fragment = getFragment(); 68 | final AndroidFile parent = context.currentTree().findByFile(fileEvent.getFile().getParentFile()); 69 | if (fragment != null && parent != null) { 70 | final AndroidFile file = createAndroidFile(fileEvent.getFile(), fileEvent.isFile(), parent); 71 | parent.getChildren().add(file); 72 | 73 | // updateView the UI because it's currently watched folder 74 | activity.runOnUiThread(new Runnable() { 75 | @Override 76 | public void run() { 77 | if (parent.equals(activity.getCurrentFolder())) { 78 | fragment.getFileAdapter().add(file); 79 | } 80 | 81 | fragment.getFileAdapter().updateView(true); 82 | } 83 | }); 84 | } 85 | } 86 | 87 | @Override 88 | @Handler 89 | public void onFileUpdate(IFileUpdateEvent fileEvent) { 90 | LOG.debug("Remotely updated file {}", fileEvent.getFile()); 91 | final AndroidFile file = context.currentTree().findByFile(fileEvent.getFile()); 92 | if (file == null) { 93 | return; 94 | } 95 | 96 | file.setState(FileState.OUTDATED); 97 | 98 | // updateView the UI when it's the currently watched folder 99 | final FilesFragment fragment = getFragment(); 100 | if (fragment != null && file.getParent().equals(activity.getCurrentFolder())) { 101 | activity.runOnUiThread(new Runnable() { 102 | @Override 103 | public void run() { 104 | file.setState(FileState.OUTDATED, fragment.getFileAdapter()); 105 | } 106 | }); 107 | } 108 | } 109 | 110 | @Override 111 | @Handler 112 | public void onFileDelete(IFileDeleteEvent fileEvent) { 113 | LOG.debug("Remotely deleted file {}", fileEvent.getFile()); 114 | final AndroidFile file = context.currentTree().findByFile(fileEvent.getFile()); 115 | if (file == null) { 116 | return; 117 | } 118 | 119 | // remove from model 120 | boolean removed = file.getParent().getChildren().remove(file); 121 | final FilesFragment fragment = getFragment(); 122 | if (removed && fragment != null) { 123 | // need to updateView the current view 124 | activity.runOnUiThread(new Runnable() { 125 | @Override 126 | public void run() { 127 | if (file.getParent().equals(activity.getCurrentFolder())) { 128 | fragment.getFileAdapter().remove(file); 129 | } 130 | fragment.getFileAdapter().updateView(false); 131 | } 132 | }); 133 | } 134 | } 135 | 136 | @Override 137 | @Handler 138 | public void onFileMove(final IFileMoveEvent fileEvent) { 139 | LOG.debug("Remotely moved file from {} to {}", fileEvent.getSrcFile(), fileEvent.getDstFile()); 140 | final AndroidFile srcFile = context.currentTree().findByFile(fileEvent.getSrcFile()); 141 | if (srcFile != null) { 142 | // for UI notification 143 | final String oldPath = srcFile.getParent().getPath(); 144 | // remove it from the old parent 145 | srcFile.getParent().getChildren().remove(srcFile); 146 | 147 | final AndroidFile dstParent = context.currentTree().findByFile(fileEvent.getDstFile().getParentFile()); 148 | if (dstParent != null) { 149 | // add the file to the new parent 150 | srcFile.setParent(dstParent); 151 | srcFile.getPermissions().clear(); 152 | srcFile.getPermissions().addAll(dstParent.getPermissions()); 153 | dstParent.getChildren().add(srcFile); 154 | 155 | final FilesFragment fragment = getFragment(); 156 | if (fragment != null) { 157 | // need to updateView the current view 158 | activity.runOnUiThread(new Runnable() { 159 | @Override 160 | public void run() { 161 | if (oldPath.equals(activity.getCurrentFolder().getPath())) { 162 | fragment.getFileAdapter().remove(srcFile); 163 | } else if (dstParent.equals(activity.getCurrentFolder())) { 164 | fragment.getFileAdapter().add(srcFile); 165 | } 166 | fragment.getFileAdapter().updateView(true); 167 | } 168 | }); 169 | } 170 | } else { 171 | LOG.warn("New parent for moved file {} not found", fileEvent.getDstFile()); 172 | } 173 | } else { 174 | LOG.warn("Original moved file {} not found. Already moved?", fileEvent.getSrcFile()); 175 | } 176 | } 177 | 178 | @Override 179 | @Handler 180 | public void onFileShare(IFileShareEvent fileEvent) { 181 | LOG.debug("Got notified that file {} has been shared by {}.", fileEvent.getFile(), fileEvent.getInvitedBy()); 182 | currentShareInvitations.put(fileEvent.getFile(), fileEvent.getUserPermissions()); 183 | // can ignore because 'addFile' will be triggered as well 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/FileArrayAdapter.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ArrayAdapter; 9 | import android.widget.ImageView; 10 | import android.widget.ProgressBar; 11 | import android.widget.TextView; 12 | 13 | import org.hive2hive.mobile.H2HApplication; 14 | import org.hive2hive.mobile.R; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * @author Nico 20 | * inspired by http://custom-android-dn.blogspot.in/2013/01/create-simple-file-explore-in-android.html 21 | */ 22 | public class FileArrayAdapter extends ArrayAdapter { 23 | 24 | private static final int LAYOUT_ID = R.layout.fragment_files; 25 | 26 | private final H2HApplication context; 27 | private final AndroidFileComparator comparator; 28 | 29 | public FileArrayAdapter(H2HApplication context, List items) { 30 | super(context, LAYOUT_ID, items); 31 | this.context = context; 32 | this.comparator = new AndroidFileComparator(); 33 | } 34 | 35 | @Override 36 | public View getView(int position, View convertView, ViewGroup parent) { 37 | View view = convertView; 38 | if (view == null) { 39 | LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 40 | view = vi.inflate(LAYOUT_ID, null); 41 | } 42 | 43 | final AndroidFile node = getItem(position); 44 | if (node != null) { 45 | TextView nameText = (TextView) view.findViewById(R.id.file_list_name); 46 | nameText.setText(node.getFile().getName()); 47 | 48 | TextView detailsText = (TextView) view.findViewById(R.id.file_list_details); 49 | detailsText.setText(getDetailsText(node)); 50 | node.setDetailsView(detailsText); 51 | 52 | // TextView dateText = (TextView) v.findViewById(R.id.file_list_date); 53 | // dateText.setText(fileTaste.getDate()); 54 | 55 | // register the image view at the file state holder in order to automatically get updates 56 | ImageView fileImage = (ImageView) view.findViewById(R.id.file_list_icon); 57 | node.setImageView(fileImage); 58 | 59 | Drawable icon = context.getResources().getDrawable(node.getState().getIconId(node.isFile())); 60 | fileImage.setImageDrawable(icon); 61 | 62 | // set the progress bar 63 | ProgressBar progress = (ProgressBar) view.findViewById(R.id.file_list_progress); 64 | node.setProgressBar(progress); 65 | } 66 | 67 | return view; 68 | } 69 | 70 | @Override 71 | public void add(AndroidFile file) { 72 | if(getPosition(file) > 0) { 73 | // don't add it because it is already in the list 74 | } else { 75 | super.add(file); 76 | } 77 | } 78 | 79 | private String getDetailsText(AndroidFile node) { 80 | if (node.isFolder()) { 81 | // show item count 82 | int items = node.getChildren().size(); 83 | if (items == 0) { 84 | return context.getString(R.string.folder_empty); 85 | } else if (items == 1) { 86 | return context.getString(R.string.folder_single_item); 87 | } else { 88 | return context.getString(R.string.folder_items, items); 89 | } 90 | } else { 91 | return context.getString(node.getState().getMessageId()); 92 | } 93 | } 94 | 95 | public void updateView(boolean sort) { 96 | if (sort) { 97 | super.sort(comparator); 98 | } else { 99 | notifyDataSetChanged(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/FileState.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files; 2 | 3 | import org.hive2hive.mobile.R; 4 | 5 | /** 6 | * @author Nico 7 | */ 8 | public enum FileState { 9 | 10 | /** 11 | * The file exists in the latest version on the disk The next state will be {@link org.hive2hive.mobile.files.FileState#OUTDATED} or {@link org.hive2hive.mobile.files.FileState#ON_AIR} if it's deleted locally 12 | */ 13 | IN_SYNC(R.drawable.ic_blank_ex, R.drawable.ic_folder, R.string.file_state_in_sync), 14 | 15 | /** 16 | * The file is currently downloading. The next state will be {@link org.hive2hive.mobile.files.FileState#IN_SYNC} 17 | */ 18 | DOWNLOADING(R.drawable.ic_download_inex, R.drawable.ic_folder_loading, R.string.file_state_downloading), 19 | 20 | /** 21 | * The file does only exist in the user profile ({@link org.hive2hive.core.processes.files.list.FileNode}, but not on the phone itself. The next state will be {@link org.hive2hive.mobile.files.FileState#DOWNLOADING} 22 | */ 23 | ON_AIR(R.drawable.ic_cloud_inex, R.drawable.ic_folder, R.string.file_state_on_air), 24 | 25 | /** 26 | * The file exists in an older version on the phone. A new version could be downloaded. Thus, the next state is {@link org.hive2hive.mobile.files.FileState#DOWNLOADING}. 27 | */ 28 | OUTDATED(R.drawable.ic_loading_inex, R.drawable.ic_folder_loading, R.string.file_state_outdated), 29 | 30 | /** 31 | * The file is currently uploading (new file or new version). Next state is {@link org.hive2hive.mobile.files.FileState#IN_SYNC}. 32 | */ 33 | UPLOADING(R.drawable.ic_upload_ex, R.drawable.ic_folder_upload, R.string.file_state_uploading), 34 | 35 | /** 36 | * The user selected the file to be deleted. There's no next state because the file is completely removed from the network. 37 | */ 38 | DELETING(R.drawable.ic_loading_inex, R.drawable.ic_folder_loading, R.string.file_state_deleting), 39 | 40 | /** 41 | * The user is currently sharing the folder with another user. The next state is again {@link org.hive2hive.mobile.files.FileState#SHARED}. 42 | */ 43 | SHARING(R.drawable.ic_loading_inex, R.drawable.ic_folder_loading, R.string.file_state_sharing), 44 | 45 | /** 46 | * This folder is shared with another user 47 | */ 48 | TOP_SHARED(R.drawable.ic_loading_inex, R.drawable.ic_folder_shared, R.string.file_state_shared), 49 | 50 | /** 51 | * The folder is currently shared with another user, but not the root share folder. 52 | */ 53 | SHARED(R.drawable.ic_loading_inex, R.drawable.ic_folder, R.string.file_state_in_sync); 54 | 55 | 56 | private final int fileIconId; 57 | private final int folderIconId; 58 | private final int messageId; 59 | 60 | private FileState(int fileIconId, int folderIconId, int messageId) { 61 | this.fileIconId = fileIconId; 62 | this.folderIconId = folderIconId; 63 | this.messageId = messageId; 64 | } 65 | 66 | public int getIconId(boolean isFile) { 67 | if (isFile) { 68 | return fileIconId; 69 | } else { 70 | return folderIconId; 71 | } 72 | } 73 | 74 | public int getMessageId() { 75 | return messageId; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/FilesFragment.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.ListFragment; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.net.Uri; 10 | import android.os.Bundle; 11 | import android.view.ContextMenu; 12 | import android.view.ContextThemeWrapper; 13 | import android.view.LayoutInflater; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.webkit.MimeTypeMap; 18 | import android.widget.AdapterView; 19 | import android.widget.CheckBox; 20 | import android.widget.EditText; 21 | import android.widget.ListView; 22 | import android.widget.Toast; 23 | 24 | import org.apache.commons.io.FilenameUtils; 25 | import org.hive2hive.core.model.PermissionType; 26 | import org.hive2hive.core.model.UserPermission; 27 | import org.hive2hive.mobile.H2HApplication; 28 | import org.hive2hive.mobile.R; 29 | import org.hive2hive.mobile.common.UserPermissionComparator; 30 | import org.hive2hive.mobile.files.tasks.FileDeleteTask; 31 | import org.hive2hive.mobile.files.tasks.FileDownloadTask; 32 | import org.hive2hive.mobile.files.tasks.FileShareTask; 33 | import org.hive2hive.mobile.files.tasks.FileUpdateTask; 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | 37 | import java.io.File; 38 | import java.util.ArrayList; 39 | import java.util.Collections; 40 | import java.util.List; 41 | 42 | 43 | public class FilesFragment extends ListFragment { 44 | 45 | private static final Logger LOG = LoggerFactory.getLogger(FilesFragment.class); 46 | 47 | private H2HApplication context; 48 | private FileArrayAdapter fileAdapter; 49 | 50 | @Override 51 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 52 | // create ContextThemeWrapper from the original Activity Context with the custom theme 53 | final Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_DARK); 54 | // clone the inflater using the ContextThemeWrapper 55 | LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper); 56 | 57 | // Inflate the layout for this fragment 58 | View view = super.onCreateView(localInflater, container, savedInstanceState); 59 | localInflater.inflate(R.layout.fragment_files, container, false); 60 | return view; 61 | } 62 | 63 | @Override 64 | public void onActivityCreated(Bundle savedInstanceState) { 65 | super.onActivityCreated(savedInstanceState); 66 | registerForContextMenu(getListView()); 67 | } 68 | 69 | public String getPath() { 70 | return getArguments().getString("path"); 71 | } 72 | 73 | public FileArrayAdapter getFileAdapter() { 74 | return fileAdapter; 75 | } 76 | 77 | private FilesActivity getFilesActivity() { 78 | return (FilesActivity) getActivity(); 79 | } 80 | 81 | public void fillFileList(AndroidFile folder, H2HApplication context) { 82 | this.context = context; 83 | if (folder == null) { 84 | return; 85 | } 86 | 87 | // Put the data into the list 88 | List children = new ArrayList<>(folder.getChildren()); 89 | fileAdapter = new FileArrayAdapter(context, children); 90 | setListAdapter(fileAdapter); 91 | } 92 | 93 | private void openFile(final File file) { 94 | Intent intent = new Intent(); 95 | intent.setAction(android.content.Intent.ACTION_VIEW); 96 | 97 | MimeTypeMap mime = MimeTypeMap.getSingleton(); 98 | String ext = FilenameUtils.getExtension(file.getName()); 99 | String type = mime.getMimeTypeFromExtension(ext); 100 | intent.setDataAndType(Uri.fromFile(file), type); 101 | 102 | try { 103 | startActivity(intent); 104 | } catch (ActivityNotFoundException ex) { 105 | // happens when the file has an unknown suffix / type 106 | Toast.makeText(getActivity(), R.string.files_error_open, Toast.LENGTH_LONG).show(); 107 | } 108 | 109 | getFilesActivity().resetLogoutHint(); 110 | } 111 | 112 | @Override 113 | public void onListItemClick(ListView l, View v, int position, long id) { 114 | AndroidFile node = (AndroidFile) getListAdapter().getItem(position); 115 | if (node.isFolder() && (node.getState() == FileState.IN_SYNC || node.getState() == FileState.SHARED || node.getState() == FileState.TOP_SHARED)) { 116 | getFilesActivity().showFolder(node); 117 | } else { 118 | FileState state = node.getState(); 119 | switch (state) { 120 | case IN_SYNC: // fall-through 121 | case SHARING: 122 | openFile(node.getFile()); 123 | break; 124 | case DOWNLOADING: 125 | // TODO show dialog to cancel download 126 | break; 127 | case ON_AIR: // fall-through 128 | case OUTDATED: 129 | new FileDownloadTask(context.h2hNode().getFileManager(), fileAdapter, node).execute(); 130 | break; 131 | } 132 | LOG.debug("Selected file '{}' for action...", node.getFile()); 133 | } 134 | } 135 | 136 | @Override 137 | public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { 138 | super.onCreateContextMenu(menu, v, menuInfo); 139 | AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; 140 | AndroidFile item = fileAdapter.getItem(info.position); 141 | 142 | switch (item.getState()) { 143 | case ON_AIR: 144 | case OUTDATED: 145 | menu.add(0, R.string.context_files_download, 0, R.string.context_files_download); 146 | if (item.canWrite(context.currentUser())) { 147 | menu.add(0, R.string.context_files_update, 0, R.string.context_files_update); 148 | } 149 | case SHARED: 150 | menu.add(0, R.string.context_files_open, 0, R.string.context_files_open); 151 | if (item.isFile()) { 152 | menu.add(0, R.string.context_files_delete_locally, 0, R.string.context_files_delete_locally); 153 | } else { 154 | menu.add(0, R.string.context_folder_share_info, 0, R.string.context_folder_share_info); 155 | } 156 | if (item.canWrite(context.currentUser())) { 157 | menu.add(0, R.string.context_delete_globally, 0, R.string.context_delete_globally); 158 | } 159 | break; 160 | case TOP_SHARED: 161 | menu.add(0, R.string.context_files_open, 0, R.string.context_files_open); 162 | menu.add(0, R.string.context_folder_share, 0, R.string.context_folder_share); 163 | menu.add(0, R.string.context_folder_share_info, 0, R.string.context_folder_share_info); 164 | menu.add(0, R.string.context_delete_globally, 0, R.string.context_delete_globally); 165 | break; 166 | case IN_SYNC: 167 | menu.add(0, R.string.context_files_open, 0, R.string.context_files_open); 168 | if (item.isFile()) { 169 | menu.add(0, R.string.context_files_delete_locally, 0, R.string.context_files_delete_locally); 170 | } else { 171 | menu.add(0, R.string.context_folder_share, 0, R.string.context_folder_share); 172 | } 173 | if (item.canWrite(context.currentUser())) { 174 | menu.add(0, R.string.context_delete_globally, 0, R.string.context_delete_globally); 175 | } 176 | break; 177 | case SHARING: 178 | case UPLOADING: 179 | case DOWNLOADING: 180 | default: 181 | if (item.canWrite(context.currentUser())) { 182 | menu.add(0, R.string.context_delete_globally, 0, R.string.context_delete_globally); 183 | } 184 | } 185 | } 186 | 187 | @Override 188 | public boolean onContextItemSelected(MenuItem item) { 189 | getFilesActivity().resetLogoutHint(); 190 | 191 | AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 192 | AndroidFile node = fileAdapter.getItem(info.position); 193 | 194 | switch (item.getItemId()) { 195 | case R.string.context_files_download: 196 | new FileDownloadTask(context.h2hNode().getFileManager(), fileAdapter, node).execute(); 197 | break; 198 | case R.string.context_files_update: 199 | new FileUpdateTask(context.h2hNode().getFileManager(), fileAdapter, node).execute(); 200 | break; 201 | case R.string.context_files_delete_locally: 202 | if (node.getFile().delete()) { 203 | node.setState(FileState.ON_AIR, fileAdapter); 204 | } else { 205 | Toast.makeText(getActivity(), R.string.files_error_delete_failed, Toast.LENGTH_SHORT).show(); 206 | } 207 | break; 208 | case R.string.context_delete_globally: 209 | if (node.isFolder() && !node.getChildren().isEmpty()) { 210 | // show warning that non-empty folders cannot be deleted 211 | Toast.makeText(getActivity(), R.string.files_error_delete_nonempty, Toast.LENGTH_SHORT).show(); 212 | } else { 213 | new FileDeleteTask(context.h2hNode().getFileManager(), fileAdapter, node).execute(); 214 | } 215 | break; 216 | case R.string.context_files_open: 217 | if (node.isFile()) { 218 | openFile(node.getFile()); 219 | } else { 220 | getFilesActivity().showFolder(node); 221 | } 222 | break; 223 | case R.string.context_folder_share: 224 | shareFolder(node); 225 | break; 226 | case R.string.context_folder_share_info: 227 | showShareInfo(node); 228 | break; 229 | default: 230 | super.onContextItemSelected(item); 231 | } 232 | 233 | return true; 234 | } 235 | 236 | public void shareFolder(final AndroidFile file) { 237 | ContextThemeWrapper wrapper = new ContextThemeWrapper(getActivity(), R.style.AppTheme); 238 | LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 239 | final View view = vi.inflate(R.layout.dialog_share, null); 240 | 241 | new AlertDialog.Builder(wrapper, AlertDialog.THEME_DEVICE_DEFAULT_DARK) 242 | .setTitle(R.string.share_folder_title) 243 | .setMessage(R.string.share_folder_message) 244 | .setView(view) 245 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 246 | public void onClick(DialogInterface dialog, int whichButton) { 247 | EditText usernameField = (EditText) view.findViewById(R.id.share_username); 248 | CheckBox writeCheckbox = (CheckBox) view.findViewById(R.id.share_write_permissions); 249 | 250 | PermissionType perm = writeCheckbox.isChecked() ? PermissionType.WRITE : PermissionType.READ; 251 | new FileShareTask(context.h2hNode().getFileManager(), fileAdapter, file, usernameField.getText().toString(), perm).execute(); 252 | } 253 | }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 254 | public void onClick(DialogInterface dialog, int whichButton) { 255 | // Do nothing. 256 | } 257 | }).show(); 258 | } 259 | 260 | private void showShareInfo(AndroidFile node) { 261 | // Concatenate user list 262 | List permissions = new ArrayList<>(node.getPermissions()); 263 | Collections.sort(permissions, new UserPermissionComparator()); 264 | StringBuilder sb = new StringBuilder(getString(R.string.share_info_message, node.getFile().getName())).append("\n"); 265 | for (UserPermission permission : permissions) { 266 | sb.append("\u2022 ").append(permission.getUserId()); 267 | if (permission.getPermission() == PermissionType.READ) { 268 | sb.append(" ").append(getString(R.string.share_info_readonly)); 269 | } 270 | sb.append("\n"); 271 | } 272 | 273 | new AlertDialog.Builder(getActivity()) 274 | .setTitle(R.string.share_info_title) 275 | .setMessage(sb.toString()).setNeutralButton(android.R.string.ok, null).show(); 276 | } 277 | 278 | } -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/BaseFileTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.os.AsyncTask; 4 | import android.view.View; 5 | 6 | import org.hive2hive.core.api.interfaces.IFileManager; 7 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 8 | import org.hive2hive.core.exceptions.NoSessionException; 9 | import org.hive2hive.mobile.files.AndroidFile; 10 | import org.hive2hive.mobile.files.FileArrayAdapter; 11 | import org.hive2hive.processframework.interfaces.IProcessComponent; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * @author Nico 17 | */ 18 | public abstract class BaseFileTask extends AsyncTask { 19 | 20 | private static final Logger LOG = LoggerFactory.getLogger(BaseFileTask.class); 21 | 22 | protected final FileArrayAdapter adapter; 23 | protected final AndroidFile file; 24 | private final IFileManager fileManager; 25 | 26 | public BaseFileTask(IFileManager fileManager, FileArrayAdapter adapter, AndroidFile file) { 27 | this.adapter = adapter; 28 | this.file = file; 29 | this.fileManager = fileManager; 30 | } 31 | 32 | @Override 33 | protected final void onPreExecute() { 34 | beforeExecution(); 35 | if (file.getProgressBar() != null) { 36 | file.getProgressBar().setVisibility(View.VISIBLE); 37 | adapter.updateView(false); 38 | } 39 | } 40 | 41 | protected abstract void beforeExecution(); 42 | 43 | @Override 44 | protected final Boolean doInBackground(Void... voids) { 45 | try { 46 | getProcess(fileManager).execute(); 47 | return true; 48 | } catch (Exception e) { 49 | LOG.error("Cannot run the file process", e); 50 | return false; 51 | } 52 | } 53 | 54 | protected abstract IProcessComponent getProcess(IFileManager fileManager) throws NoSessionException, NoPeerConnectionException; 55 | 56 | @Override 57 | protected final void onPostExecute(Boolean success) { 58 | super.onPostExecute(success); 59 | if (file.getProgressBar() != null) { 60 | file.getProgressBar().setVisibility(View.INVISIBLE); 61 | adapter.updateView(false); 62 | } 63 | 64 | afterExecution(success); 65 | } 66 | 67 | protected abstract void afterExecution(boolean success); 68 | } 69 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/FileDeleteTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.widget.Toast; 4 | 5 | import org.hive2hive.core.api.interfaces.IFileManager; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.mobile.R; 9 | import org.hive2hive.mobile.files.AndroidFile; 10 | import org.hive2hive.mobile.files.FileArrayAdapter; 11 | import org.hive2hive.mobile.files.FileState; 12 | import org.hive2hive.processframework.interfaces.IProcessComponent; 13 | 14 | /** 15 | * @author Nico 16 | */ 17 | public class FileDeleteTask extends BaseFileTask { 18 | 19 | private FileState stateBefore; 20 | 21 | public FileDeleteTask(IFileManager fileManager, FileArrayAdapter adapter, AndroidFile file) { 22 | super(fileManager, adapter, file); 23 | } 24 | 25 | @Override 26 | protected void beforeExecution() { 27 | stateBefore = file.getState(); 28 | file.setState(FileState.DELETING, adapter); 29 | } 30 | 31 | @Override 32 | protected IProcessComponent getProcess(IFileManager fileManager) throws NoSessionException, NoPeerConnectionException { 33 | return fileManager.createDeleteProcess(file.getFile()); 34 | } 35 | 36 | @Override 37 | protected void afterExecution(boolean success) { 38 | if (success) { 39 | file.getFile().delete(); 40 | adapter.remove(file); 41 | file.getParent().getChildren().remove(file); 42 | } else { 43 | file.setState(stateBefore, adapter); 44 | Toast.makeText(adapter.getContext(), adapter.getContext().getString(R.string.files_error_delete_failed), Toast.LENGTH_LONG).show(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/FileDownloadTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.widget.Toast; 4 | 5 | import org.hive2hive.core.api.interfaces.IFileManager; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.mobile.R; 9 | import org.hive2hive.mobile.files.AndroidFile; 10 | import org.hive2hive.mobile.files.FileArrayAdapter; 11 | import org.hive2hive.mobile.files.FileState; 12 | import org.hive2hive.processframework.interfaces.IProcessComponent; 13 | 14 | /** 15 | * @author Nico 16 | */ 17 | public class FileDownloadTask extends BaseFileTask { 18 | 19 | public FileDownloadTask(IFileManager fileManager, FileArrayAdapter adapter, AndroidFile file) { 20 | super(fileManager, adapter, file); 21 | } 22 | 23 | @Override 24 | protected void beforeExecution() { 25 | file.setState(FileState.DOWNLOADING, adapter); 26 | } 27 | 28 | @Override 29 | protected IProcessComponent getProcess(IFileManager fileManager) throws NoSessionException, NoPeerConnectionException { 30 | return fileManager.createDownloadProcess(file.getFile()); 31 | } 32 | 33 | @Override 34 | protected void afterExecution(boolean success) { 35 | if (success) { 36 | file.setState(FileState.IN_SYNC, adapter); 37 | } else { 38 | file.setState(FileState.ON_AIR, adapter); 39 | Toast.makeText(adapter.getContext(), adapter.getContext().getString(R.string.files_error_download, file.getPath()), Toast.LENGTH_LONG).show(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/FileListTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.app.ProgressDialog; 4 | 5 | import org.hive2hive.core.api.interfaces.IFileManager; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.core.processes.files.list.FileNode; 9 | import org.hive2hive.mobile.H2HApplication; 10 | import org.hive2hive.mobile.R; 11 | import org.hive2hive.mobile.common.BaseProgressTask; 12 | import org.hive2hive.mobile.common.ISuccessFailListener; 13 | import org.hive2hive.mobile.files.AndroidFile; 14 | import org.hive2hive.processframework.exceptions.InvalidProcessStateException; 15 | import org.hive2hive.processframework.exceptions.ProcessExecutionException; 16 | import org.hive2hive.processframework.interfaces.IProcessComponent; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | /** 21 | * @author Nico 22 | */ 23 | public class FileListTask extends BaseProgressTask { 24 | 25 | private static final Logger LOG = LoggerFactory.getLogger(FileListTask.class); 26 | 27 | public FileListTask(H2HApplication context, ISuccessFailListener listener, ProgressDialog progressDialog) { 28 | super(context, listener, progressDialog); 29 | } 30 | 31 | @Override 32 | protected String[] getProgressMessages() { 33 | return new String[]{context.getString(R.string.progress_files_title)}; 34 | } 35 | 36 | @Override 37 | protected Boolean doInBackground(Void... voids) { 38 | if (context.h2hNode() != null && context.h2hNode().isConnected()) { 39 | IFileManager fileManager = context.h2hNode().getFileManager(); 40 | try { 41 | IProcessComponent process = fileManager.createFileListProcess(); 42 | FileNode root = process.execute(); 43 | AndroidFile androidRoot = new AndroidFile(root, null); 44 | context.currentTree(androidRoot); 45 | LOG.debug("Successfully obtained the file list"); 46 | } catch (NoPeerConnectionException | NoSessionException | InvalidProcessStateException | ProcessExecutionException e) { 47 | LOG.error("Cannot obtain the latest file list"); 48 | return false; 49 | } 50 | 51 | return true; 52 | } 53 | 54 | return false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/FileShareTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.widget.Toast; 4 | 5 | import org.hive2hive.core.api.interfaces.IFileManager; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.core.model.PermissionType; 9 | import org.hive2hive.core.model.UserPermission; 10 | import org.hive2hive.mobile.R; 11 | import org.hive2hive.mobile.files.AndroidFile; 12 | import org.hive2hive.mobile.files.FileArrayAdapter; 13 | import org.hive2hive.mobile.files.FileState; 14 | import org.hive2hive.processframework.interfaces.IProcessComponent; 15 | 16 | /** 17 | * @author Nico 18 | */ 19 | public class FileShareTask extends BaseFileTask { 20 | 21 | private final String username; 22 | private final PermissionType permission; 23 | private final FileState stateBefore; 24 | 25 | public FileShareTask(IFileManager fileManager, FileArrayAdapter adapter, AndroidFile file, String username, PermissionType permission) { 26 | super(fileManager, adapter, file); 27 | this.username = username; 28 | this.permission = permission; 29 | this.stateBefore = file.getState(); 30 | } 31 | 32 | @Override 33 | protected void beforeExecution() { 34 | file.setState(FileState.SHARING, adapter); 35 | } 36 | 37 | @Override 38 | protected IProcessComponent getProcess(IFileManager fileManager) throws NoSessionException, NoPeerConnectionException { 39 | return fileManager.createShareProcess(file.getFile(), username, permission); 40 | } 41 | 42 | @Override 43 | protected void afterExecution(boolean success) { 44 | if (success) { 45 | file.setState(FileState.TOP_SHARED, adapter); 46 | file.getPermissions().add(new UserPermission(username, permission)); 47 | } else { 48 | file.setState(stateBefore, adapter); 49 | Toast.makeText(adapter.getContext(), adapter.getContext().getString(R.string.files_error_share, username), Toast.LENGTH_LONG).show(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/FileUpdateTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.widget.Toast; 4 | 5 | import org.hive2hive.core.api.interfaces.IFileManager; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.mobile.R; 9 | import org.hive2hive.mobile.files.AndroidFile; 10 | import org.hive2hive.mobile.files.FileArrayAdapter; 11 | import org.hive2hive.mobile.files.FileState; 12 | import org.hive2hive.processframework.interfaces.IProcessComponent; 13 | 14 | /** 15 | * @author Nico 16 | */ 17 | public class FileUpdateTask extends BaseFileTask { 18 | 19 | public FileUpdateTask(IFileManager fileManager, FileArrayAdapter adapter, AndroidFile file) { 20 | super(fileManager, adapter, file); 21 | } 22 | 23 | @Override 24 | protected void beforeExecution() { 25 | file.setState(FileState.UPLOADING, adapter); 26 | } 27 | 28 | @Override 29 | protected IProcessComponent getProcess(IFileManager fileManager) throws NoSessionException, NoPeerConnectionException { 30 | return fileManager.createUpdateProcess(file.getFile()); 31 | } 32 | 33 | @Override 34 | protected void afterExecution(boolean success) { 35 | if (success) { 36 | file.setState(FileState.IN_SYNC, adapter); 37 | } else { 38 | file.setState(FileState.OUTDATED, adapter); 39 | Toast.makeText(adapter.getContext(), adapter.getContext().getString(R.string.files_error_upload, file.getPath()), Toast.LENGTH_LONG).show(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/files/tasks/FileUploadTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.files.tasks; 2 | 3 | import android.widget.Toast; 4 | 5 | import org.hive2hive.core.api.interfaces.IFileManager; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.mobile.R; 9 | import org.hive2hive.mobile.files.AndroidFile; 10 | import org.hive2hive.mobile.files.FileArrayAdapter; 11 | import org.hive2hive.mobile.files.FileState; 12 | import org.hive2hive.processframework.interfaces.IProcessComponent; 13 | 14 | /** 15 | * @author Nico 16 | */ 17 | public class FileUploadTask extends BaseFileTask { 18 | 19 | public FileUploadTask(IFileManager fileManager, FileArrayAdapter adapter, AndroidFile file) { 20 | super(fileManager, adapter, file); 21 | } 22 | 23 | @Override 24 | protected void beforeExecution() { 25 | // add the file to the list 26 | file.setState(FileState.UPLOADING, adapter); 27 | 28 | file.getParent().getChildren().add(file); 29 | adapter.add(file); 30 | adapter.updateView(true); 31 | } 32 | 33 | @Override 34 | protected IProcessComponent getProcess(IFileManager fileManager) throws NoSessionException, NoPeerConnectionException { 35 | return fileManager.createAddProcess(file.getFile()); 36 | } 37 | 38 | @Override 39 | protected void afterExecution(boolean success) { 40 | if (success) { 41 | file.setState(FileState.IN_SYNC, adapter); 42 | } else { 43 | file.getParent().getChildren().remove(file); 44 | adapter.remove(file); 45 | adapter.updateView(false); 46 | Toast.makeText(adapter.getContext(), adapter.getContext().getString(R.string.files_error_upload, file.getPath()), Toast.LENGTH_LONG).show(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/gcm/GCMBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.gcm; 2 | 3 | import android.app.Activity; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.support.v4.content.WakefulBroadcastReceiver; 8 | 9 | /** 10 | * Created by Nico Rutishauser on 08.09.14. 11 | * Handles incoming messages and passes them to the intent service. This class ensures that the application stays awake while the message is processed. 12 | */ 13 | public class GCMBroadcastReceiver extends WakefulBroadcastReceiver { 14 | 15 | @Override 16 | public void onReceive(Context context, Intent intent) { 17 | // Explicitly specify that GcmIntentService will handle the intent. 18 | ComponentName comp = new ComponentName(context.getPackageName(), 19 | GCMIntentService.class.getName()); 20 | 21 | // Start the service, keeping the device awake while it is launching. 22 | startWakefulService(context, (intent.setComponent(comp))); 23 | setResultCode(Activity.RESULT_OK); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/gcm/GCMIntentService.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.gcm; 2 | 3 | import android.app.IntentService; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import com.google.android.gms.gcm.GoogleCloudMessaging; 8 | 9 | import net.tomp2p.relay.buffer.BufferRequestListener; 10 | 11 | import org.hive2hive.mobile.H2HApplication; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | /** 16 | * Created by Nico Rutishauser on 08.09.14. 17 | * Handles incoming GCM messages 18 | */ 19 | public class GCMIntentService extends IntentService { 20 | 21 | private static final Logger LOG = LoggerFactory.getLogger(GCMIntentService.class); 22 | 23 | public GCMIntentService() { 24 | super("GCMIntentService"); 25 | } 26 | 27 | @Override 28 | protected void onHandleIntent(Intent intent) { 29 | GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); 30 | // the message type is in the intent 31 | String messageType = gcm.getMessageType(intent); 32 | 33 | Bundle extras = intent.getExtras(); 34 | if (extras.isEmpty()) { 35 | LOG.warn("Received empty GCM message of type {}", messageType); 36 | return; 37 | } 38 | 39 | LOG.debug("Received message of type {} with extras {}", messageType, extras.toString()); 40 | 41 | H2HApplication application = (H2HApplication) getApplicationContext(); 42 | BufferRequestListener handler = application.bufferListener(); 43 | if (handler == null) { 44 | LOG.warn("Ignoring message because peer is not connected yet / anymore"); 45 | } else { 46 | LOG.debug("Passing GCM message to handler"); 47 | handler.sendBufferRequest(extras.getString("collapse_key")); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/gcm/GCMRegistrationUtil.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.gcm; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | 8 | import com.google.android.gms.gcm.GoogleCloudMessaging; 9 | 10 | import org.hive2hive.mobile.connection.ConnectActivity; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.io.IOException; 15 | 16 | /** 17 | * Created by Nico Rutishauser on 08.09.14. 18 | *

19 | * If the application is running for the first time on this device, a new registration ID will be created by GCM. 20 | * Else, the existing ID will be used. Then, this id is forwarded to all relay peers. 21 | */ 22 | public class GCMRegistrationUtil { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(GCMRegistrationUtil.class); 25 | private static final String PROPERTY_REG_ID = "registration_id"; 26 | private static final String PROPERTY_APP_VERSION = "app_version"; 27 | private static final String PROPERTY_GCM_SENDER = "gcm_sender"; 28 | 29 | private GCMRegistrationUtil() { 30 | // only static method 31 | } 32 | 33 | /** 34 | * Obtain the registration ID from the application store or create a new one. 35 | */ 36 | public static String getRegistrationId(Context context, long gcmSenderId) { 37 | int currentAppVersion = getAppVersion(context); 38 | String registrationID = getStoredRegistrationId(context, currentAppVersion, gcmSenderId); 39 | if (registrationID == null || registrationID.isEmpty()) { 40 | registrationID = obtainRegistrationId(context, gcmSenderId); 41 | LOG.debug("Successfully obtained a new registration ID {} for version {} and sender id {}", registrationID, currentAppVersion, gcmSenderId); 42 | storeRegistrationId(context, registrationID, currentAppVersion, gcmSenderId); 43 | } else { 44 | LOG.debug("Reusing existing registration ID: {}", registrationID); 45 | } 46 | 47 | return registrationID; 48 | } 49 | 50 | /** 51 | * @return Application's version code from the {@code PackageManager}. 52 | */ 53 | private static int getAppVersion(Context context) { 54 | try { 55 | PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 56 | return packageInfo.versionCode; 57 | } catch (PackageManager.NameNotFoundException e) { 58 | // should never happen 59 | throw new RuntimeException("Could not get package name: " + e); 60 | } 61 | } 62 | 63 | private static String getStoredRegistrationId(Context context, int currentAppVersion, long gcmSenderId) { 64 | SharedPreferences prefs = context.getSharedPreferences(ConnectActivity.class.getSimpleName(), Context.MODE_PRIVATE); 65 | String registrationId = prefs.getString(PROPERTY_REG_ID, null); 66 | if (registrationId == null) { 67 | LOG.info("No registration id stored. Obtain new one"); 68 | return null; 69 | } 70 | 71 | // Check if the app version and the gcm sender id of the registered prefs match 72 | int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); 73 | if (registeredVersion != currentAppVersion) { 74 | LOG.info("App version changed from {} to {}! Obtain new registration id.", registeredVersion, currentAppVersion); 75 | return null; 76 | } 77 | 78 | long registeredGcmSenderId = prefs.getLong(PROPERTY_GCM_SENDER, Long.MIN_VALUE); 79 | if (registeredGcmSenderId != gcmSenderId) { 80 | LOG.info("GCM sender id {} does not match with stored one ({}). Obtain new registration id.", gcmSenderId, registeredGcmSenderId); 81 | return null; 82 | } 83 | 84 | return registrationId; 85 | } 86 | 87 | private static void storeRegistrationId(Context context, String registrationId, int currentAppVersion, long gcmSenderId) { 88 | SharedPreferences prefs = context.getSharedPreferences(ConnectActivity.class.getSimpleName(), Context.MODE_PRIVATE); 89 | prefs.edit().putString(PROPERTY_REG_ID, registrationId).putInt(PROPERTY_APP_VERSION, currentAppVersion).putLong(PROPERTY_GCM_SENDER, gcmSenderId).commit(); 90 | } 91 | 92 | private static String obtainRegistrationId(Context context, long gcmSenderId) { 93 | GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context); 94 | 95 | try { 96 | return gcm.register(String.valueOf(gcmSenderId)); 97 | } catch (IOException e) { 98 | LOG.error("Cannot obtain a registration ID for sender id {}", gcmSenderId, e); 99 | return null; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/login/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.login; 2 | 3 | import android.app.Activity; 4 | import android.app.ProgressDialog; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.preference.PreferenceManager; 10 | import android.text.TextUtils; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.view.Window; 15 | import android.widget.CheckBox; 16 | import android.widget.EditText; 17 | import android.widget.TextView; 18 | 19 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 20 | import org.hive2hive.mobile.H2HApplication; 21 | import org.hive2hive.mobile.R; 22 | import org.hive2hive.mobile.common.ISuccessFailListener; 23 | import org.hive2hive.mobile.connection.ConnectActivity; 24 | import org.hive2hive.mobile.files.FilesActivity; 25 | import org.hive2hive.mobile.preference.SettingsActivity; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | 30 | /** 31 | * A login screen that offers login via username/password. 32 | * 33 | * @author Nico 34 | */ 35 | public class LoginActivity extends Activity { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(LoginActivity.class); 38 | private static final String STORE_CREDENTIALS = "store"; 39 | private static final String STORED_USER_ID = "userid"; 40 | private static final String STORED_PASSWORD = "pin"; 41 | private static final String STORED_PIN = "password"; 42 | 43 | private H2HApplication context; 44 | 45 | // UI references. 46 | private EditText usernameView; 47 | private EditText passwordView; 48 | private EditText pinView; 49 | private CheckBox storeCredentials; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | requestWindowFeature(Window.FEATURE_ACTION_BAR); 55 | setContentView(R.layout.activity_login); 56 | 57 | context = (H2HApplication) getApplicationContext(); 58 | if (context.h2hNode() == null || !context.h2hNode().isConnected()) { 59 | // head back to the connection page 60 | Intent intent = new Intent(context, ConnectActivity.class); 61 | startActivity(intent); 62 | return; 63 | } 64 | 65 | // Set up the login form. 66 | usernameView = (EditText) findViewById(R.id.username); 67 | passwordView = (EditText) findViewById(R.id.password); 68 | pinView = (EditText) findViewById(R.id.pin); 69 | storeCredentials = (CheckBox) findViewById(R.id.store_credentials); 70 | 71 | // set stored credentials 72 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 73 | usernameView.setText(prefs.getString(STORED_USER_ID, ""), TextView.BufferType.EDITABLE); 74 | passwordView.setText(prefs.getString(STORED_PASSWORD, ""), TextView.BufferType.EDITABLE); 75 | pinView.setText(prefs.getString(STORED_PIN, ""), TextView.BufferType.EDITABLE); 76 | storeCredentials.setChecked(prefs.getBoolean(STORE_CREDENTIALS, false)); 77 | } 78 | 79 | /** 80 | * Attempts to sign in or register the account specified by the login form. 81 | * If there are form errors (invalid username, missing fields, etc.), the 82 | * errors are presented and no actual login attempt is made. 83 | */ 84 | public void login(View view) { 85 | if (context.h2hNode() == null) { 86 | return; 87 | } 88 | 89 | try { 90 | if (context.h2hNode().getUserManager().isLoggedIn()) { 91 | // already logged in 92 | startFileView(); 93 | return; 94 | } 95 | } catch (NoPeerConnectionException e) { 96 | LOG.warn("Cannot determine the login state", e); 97 | } 98 | 99 | // Reset errors. 100 | usernameView.setError(null); 101 | passwordView.setError(null); 102 | 103 | // Store values at the time of the login attempt. 104 | String username = usernameView.getText().toString(); 105 | String password = passwordView.getText().toString(); 106 | String pin = pinView.getText().toString(); 107 | 108 | boolean cancel = false; 109 | View focusView = null; 110 | 111 | 112 | // Check for a valid password, if the user entered one. 113 | if (TextUtils.isEmpty(password)) { 114 | passwordView.setError(getString(R.string.error_field_required)); 115 | focusView = passwordView; 116 | cancel = true; 117 | } 118 | 119 | // Check for a valid pin, if the user entered one. 120 | if (TextUtils.isEmpty(pin)) { 121 | pinView.setError(getString(R.string.error_field_required)); 122 | focusView = pinView; 123 | cancel = true; 124 | } 125 | 126 | // Check for a valid username address. 127 | if (TextUtils.isEmpty(username)) { 128 | usernameView.setError(getString(R.string.error_field_required)); 129 | focusView = usernameView; 130 | cancel = true; 131 | } 132 | 133 | if (cancel) { 134 | // There was an error; don't attempt login and focus the first 135 | // form field with an error. 136 | focusView.requestFocus(); 137 | } else { 138 | // create the progress dialog 139 | ProgressDialog dialog = new ProgressDialog(this); 140 | dialog.setTitle(getString(R.string.progress_login_title)); 141 | dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 142 | dialog.setCancelable(false); 143 | dialog.setCanceledOnTouchOutside(false); 144 | 145 | // Show a progress spinner, and kick off a background task to 146 | // perform the user login attempt. 147 | UserLoginTask loginTask = new UserLoginTask(context, username, password, pin, new LoginListener(username, password, pin), dialog); 148 | loginTask.execute((Void) null); 149 | } 150 | } 151 | 152 | private void startFileView() { 153 | Intent intent = new Intent(getApplicationContext(), FilesActivity.class); 154 | intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 155 | startActivity(intent); 156 | } 157 | 158 | private void storeCredentials(String username, String password, String pin) { 159 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 160 | if (storeCredentials.isChecked()) { 161 | // store the credentials 162 | prefs.edit().putBoolean(STORE_CREDENTIALS, true).putString(STORED_USER_ID, username).putString(STORED_PASSWORD, password).putString(STORED_PIN, pin).commit(); 163 | } else { 164 | // erase existing credentials 165 | prefs.edit().putBoolean(STORE_CREDENTIALS, false).remove(STORED_USER_ID).remove(STORED_PASSWORD).remove(STORED_PIN).commit(); 166 | } 167 | } 168 | 169 | @Override 170 | public boolean onCreateOptionsMenu(Menu menu) { 171 | getMenuInflater().inflate(R.menu.menu_settings, menu); 172 | return true; 173 | } 174 | 175 | @Override 176 | public boolean onOptionsItemSelected(MenuItem item) { 177 | // Handle action bar item clicks here. The action bar will 178 | // automatically handle clicks on the Home/Up button, so long 179 | // as you specify a parent activity in AndroidManifest.xml. 180 | int id = item.getItemId(); 181 | if (id == R.id.action_settings) { 182 | Intent intent = new Intent(getApplicationContext(), SettingsActivity.class); 183 | startActivity(intent); 184 | return true; 185 | } else if (id == R.id.action_help) { 186 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help_login_url))); 187 | startActivity(browserIntent); 188 | return true; 189 | } 190 | 191 | return super.onOptionsItemSelected(item); 192 | } 193 | 194 | private class LoginListener implements ISuccessFailListener { 195 | 196 | private final String username; 197 | private final String password; 198 | private final String pin; 199 | 200 | public LoginListener(String username, String password, String pin) { 201 | this.username = username; 202 | this.password = password; 203 | this.pin = pin; 204 | } 205 | 206 | @Override 207 | public void onSuccess() { 208 | storeCredentials(username, password, pin); 209 | startFileView(); 210 | } 211 | 212 | @Override 213 | public void onFail() { 214 | passwordView.setError(getString(R.string.error_incorrect_credentials)); 215 | passwordView.requestFocus(); 216 | } 217 | } 218 | } 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/login/UserLoginTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.login; 2 | 3 | import android.app.ProgressDialog; 4 | 5 | import org.hive2hive.core.api.interfaces.IH2HNode; 6 | import org.hive2hive.core.api.interfaces.IUserManager; 7 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 8 | import org.hive2hive.core.security.UserCredentials; 9 | import org.hive2hive.mobile.H2HApplication; 10 | import org.hive2hive.mobile.R; 11 | import org.hive2hive.mobile.common.AndroidFileAgent; 12 | import org.hive2hive.mobile.common.BaseProgressTask; 13 | import org.hive2hive.mobile.common.ISuccessFailListener; 14 | import org.hive2hive.processframework.exceptions.InvalidProcessStateException; 15 | import org.hive2hive.processframework.exceptions.ProcessExecutionException; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | /** 20 | * Represents an asynchronous login/registration task used to authenticate 21 | * the user. 22 | */ 23 | public class UserLoginTask extends BaseProgressTask { 24 | private static final Logger LOG = LoggerFactory.getLogger(UserLoginTask.class); 25 | 26 | private final String username; 27 | private final String password; 28 | private final String pin; 29 | 30 | public UserLoginTask(H2HApplication context, String username, String password, String pin, ISuccessFailListener listener, ProgressDialog progressDialog) { 31 | super(context, listener, progressDialog); 32 | this.username = username; 33 | this.password = password; 34 | this.pin = pin; 35 | } 36 | 37 | @Override 38 | protected String[] getProgressMessages() { 39 | String[] progressMessages = new String[4]; 40 | progressMessages[0] = context.getString(R.string.progress_login_encrypt); 41 | progressMessages[1] = context.getString(R.string.progress_login_register_check); 42 | progressMessages[2] = context.getString(R.string.progress_login_register); 43 | progressMessages[3] = context.getString(R.string.progress_login_login); 44 | return progressMessages; 45 | } 46 | 47 | @Override 48 | protected Boolean doInBackground(Void... params) { 49 | IH2HNode node = context.h2hNode(); 50 | if (node == null || !node.isConnected()) { 51 | LOG.error("H2HNode is null or not connected (anymore)"); 52 | // TODO head back to the connection activity 53 | return false; 54 | } 55 | 56 | IUserManager userManager = node.getUserManager(); 57 | 58 | // create credentials here (takes some time) 59 | publishProgress(0); 60 | UserCredentials credentials = new UserCredentials(username, password, pin); 61 | 62 | try { 63 | LOG.debug("Check if user {} is already registered.", username); 64 | publishProgress(1); 65 | if (!userManager.isRegistered(username)) { 66 | LOG.debug("Start registering user {}.", username); 67 | publishProgress(2); 68 | userManager.createRegisterProcess(credentials).execute(); 69 | LOG.debug("User {} successfully registered.", username); 70 | } 71 | } catch (NoPeerConnectionException | ProcessExecutionException | InvalidProcessStateException e) { 72 | LOG.error("Cannot check if user registered or cannot register user {}", username, e); 73 | return false; 74 | } 75 | 76 | try { 77 | LOG.debug("Start logging in user {}", username); 78 | publishProgress(3); 79 | AndroidFileAgent fileAgent = new AndroidFileAgent(context, username); 80 | userManager.createLoginProcess(credentials, fileAgent).execute(); 81 | LOG.info("User {} successfully logged in", username); 82 | context.currentUser(username); 83 | return true; 84 | } catch (NoPeerConnectionException | ProcessExecutionException | InvalidProcessStateException e) { 85 | LOG.error("Cannot login user {}", credentials.getUserId(), e); 86 | return false; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/login/UserLogoutTask.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.login; 2 | 3 | import android.app.ProgressDialog; 4 | 5 | import org.hive2hive.core.api.interfaces.IH2HNode; 6 | import org.hive2hive.core.exceptions.NoPeerConnectionException; 7 | import org.hive2hive.core.exceptions.NoSessionException; 8 | import org.hive2hive.mobile.H2HApplication; 9 | import org.hive2hive.mobile.R; 10 | import org.hive2hive.mobile.common.BaseProgressTask; 11 | import org.hive2hive.mobile.common.ISuccessFailListener; 12 | import org.hive2hive.processframework.exceptions.InvalidProcessStateException; 13 | import org.hive2hive.processframework.exceptions.ProcessExecutionException; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | * Represents an asynchronous logout task 19 | */ 20 | public class UserLogoutTask extends BaseProgressTask { 21 | private static final Logger LOG = LoggerFactory.getLogger(UserLogoutTask.class); 22 | 23 | public UserLogoutTask(H2HApplication context, ISuccessFailListener listener, ProgressDialog progressDialog) { 24 | super(context, listener, progressDialog); 25 | } 26 | 27 | @Override 28 | protected String[] getProgressMessages() { 29 | String[] progressMessages = new String[1]; 30 | progressMessages[0] = context.getString(R.string.progress_logout_msg); 31 | return progressMessages; 32 | } 33 | 34 | @Override 35 | protected Boolean doInBackground(Void... params) { 36 | IH2HNode node = context.h2hNode(); 37 | if (node == null || !node.isConnected()) { 38 | LOG.error("H2HNode is null or not connected (anymore)"); 39 | // TODO head back to the connection activity 40 | return false; 41 | } 42 | 43 | try { 44 | if (!node.getUserManager().isLoggedIn()) { 45 | LOG.info("Not logged in"); 46 | return true; 47 | } 48 | LOG.debug("Start logging out..."); 49 | node.getUserManager().createLogoutProcess().execute(); 50 | LOG.debug("Successfully logged out"); 51 | return true; 52 | } catch (InvalidProcessStateException | ProcessExecutionException | NoPeerConnectionException | NoSessionException e) { 53 | LOG.error("Cannot logout properly", e); 54 | return false; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/preference/PortPickerPreference.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.preference; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.preference.DialogPreference; 6 | import android.util.AttributeSet; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.FrameLayout; 11 | import android.widget.NumberPicker; 12 | 13 | import org.hive2hive.core.H2HConstants; 14 | 15 | /** 16 | * A {@link android.preference.Preference} that displays a number picker as a dialog. 17 | */ 18 | public class PortPickerPreference extends DialogPreference { 19 | 20 | public static final int MAX_VALUE = 65535; 21 | public static final int MIN_VALUE = 1; 22 | public static final int DEFAULT_PORT = H2HConstants.H2H_PORT; 23 | 24 | private NumberPicker picker; 25 | private int value; 26 | 27 | public PortPickerPreference(Context context, AttributeSet attrs) { 28 | super(context, attrs); 29 | } 30 | 31 | public PortPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | } 34 | 35 | @Override 36 | protected View onCreateDialogView() { 37 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( 38 | ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 39 | layoutParams.gravity = Gravity.CENTER; 40 | 41 | picker = new NumberPicker(getContext()); 42 | picker.setLayoutParams(layoutParams); 43 | 44 | FrameLayout dialogView = new FrameLayout(getContext()); 45 | dialogView.addView(picker); 46 | 47 | return dialogView; 48 | } 49 | 50 | @Override 51 | protected void onBindDialogView(View view) { 52 | super.onBindDialogView(view); 53 | picker.setMinValue(MIN_VALUE); 54 | picker.setMaxValue(MAX_VALUE); 55 | picker.setValue(getValue()); 56 | } 57 | 58 | @Override 59 | protected void onDialogClosed(boolean positiveResult) { 60 | if (positiveResult) { 61 | setValue(picker.getValue()); 62 | } 63 | } 64 | 65 | @Override 66 | protected Object onGetDefaultValue(TypedArray a, int index) { 67 | return a.getInt(index, DEFAULT_PORT); 68 | } 69 | 70 | @Override 71 | protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 72 | setValue(restorePersistedValue ? getPersistedInt(DEFAULT_PORT) : (Integer) defaultValue); 73 | } 74 | 75 | public void setValue(int value) { 76 | this.value = value; 77 | persistInt(this.value); 78 | } 79 | 80 | public int getValue() { 81 | return this.value; 82 | } 83 | } -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/preference/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.preference; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import org.hive2hive.mobile.R; 7 | 8 | /** 9 | * @author Nico 10 | */ 11 | public class SettingsActivity extends Activity { 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setTitle(R.string.action_settings); 16 | 17 | // Display the fragment as the main content. 18 | getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit(); 19 | } 20 | } -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/preference/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.preference; 2 | 3 | import android.content.SharedPreferences; 4 | import android.content.pm.PackageManager; 5 | import android.os.Bundle; 6 | import android.preference.EditTextPreference; 7 | import android.preference.ListPreference; 8 | import android.preference.Preference; 9 | import android.preference.PreferenceCategory; 10 | import android.preference.PreferenceFragment; 11 | 12 | import org.hive2hive.mobile.BuildConfig; 13 | import org.hive2hive.mobile.H2HApplication; 14 | import org.hive2hive.mobile.R; 15 | import org.hive2hive.mobile.common.AndroidFileAgent; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * @author Nico 23 | * Inspired by http://www.vogella.com/tutorials/AndroidFileBasedPersistence/article.html 24 | */ 25 | public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(SettingsFragment.class); 28 | 29 | @Override 30 | public void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | addPreferencesFromResource(R.xml.preferences); 33 | 34 | // show the current value in the settings screen 35 | for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { 36 | initSummary(getPreferenceScreen().getPreference(i)); 37 | } 38 | 39 | try { 40 | String appVersion = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName; 41 | findPreference(getString(R.string.pref_app_version_key)).setSummary(appVersion); 42 | } catch (PackageManager.NameNotFoundException e) { 43 | LOG.warn("Cannot get the application version", e); 44 | } 45 | 46 | findPreference(getString(R.string.pref_h2h_version_key)).setSummary(BuildConfig.H2H_VERSION); 47 | 48 | // depends whether the user is logged in or not 49 | H2HApplication context = (H2HApplication) getActivity().getApplicationContext(); 50 | File root = AndroidFileAgent.getStorageLocation(context, context.currentUser()); 51 | findPreference(getString(R.string.pref_path_key)).setSummary(root.getAbsolutePath()); 52 | 53 | } 54 | 55 | @Override 56 | public void onResume() { 57 | super.onResume(); 58 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 59 | } 60 | 61 | @Override 62 | public void onPause() { 63 | super.onPause(); 64 | getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); 65 | } 66 | 67 | @Override 68 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 69 | updatePreferences(findPreference(key)); 70 | } 71 | 72 | private void initSummary(Preference p) { 73 | if (p instanceof PreferenceCategory) { 74 | PreferenceCategory cat = (PreferenceCategory) p; 75 | for (int i = 0; i < cat.getPreferenceCount(); i++) { 76 | initSummary(cat.getPreference(i)); 77 | } 78 | } else { 79 | updatePreferences(p); 80 | } 81 | } 82 | 83 | private void updatePreferences(Preference p) { 84 | if (p instanceof EditTextPreference) { 85 | EditTextPreference editTextPref = (EditTextPreference) p; 86 | p.setSummary(editTextPref.getText()); 87 | } else if (p instanceof PortPickerPreference) { 88 | PortPickerPreference ppPref = (PortPickerPreference) p; 89 | p.setSummary(String.valueOf(ppPref.getValue())); 90 | } else if (p instanceof ListPreference) { 91 | ListPreference lPref = ((ListPreference) p); 92 | p.setSummary(lPref.getEntry()); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/security/SCSecurityClassProvider.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.security; 2 | 3 | import org.hive2hive.core.serializer.ISecurityClassProvider; 4 | import org.spongycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey; 5 | import org.spongycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey; 6 | import org.spongycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey; 7 | import org.spongycastle.jce.provider.BouncyCastleProvider; 8 | 9 | import java.security.interfaces.RSAPrivateCrtKey; 10 | import java.security.interfaces.RSAPrivateKey; 11 | import java.security.interfaces.RSAPublicKey; 12 | 13 | /** 14 | * @author Nico 15 | */ 16 | public class SCSecurityClassProvider implements ISecurityClassProvider { 17 | 18 | public static final String SECURITY_PROVIDER = BouncyCastleProvider.PROVIDER_NAME; 19 | 20 | @Override 21 | public String getSecurityProvider() { 22 | return SECURITY_PROVIDER; 23 | } 24 | 25 | @Override 26 | public Class getRSAPublicKeyClass() { 27 | return BCRSAPublicKey.class; 28 | } 29 | 30 | @Override 31 | public Class getRSAPrivateKeyClass() { 32 | return BCRSAPrivateKey.class; 33 | } 34 | 35 | @Override 36 | public Class getRSAPrivateCrtKeyClass() { 37 | return BCRSAPrivateCrtKey.class; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/security/SCStrongAESEncryption.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.security; 2 | 3 | import org.hive2hive.core.security.IStrongAESEncryption; 4 | import org.spongycastle.crypto.CipherParameters; 5 | import org.spongycastle.crypto.DataLengthException; 6 | import org.spongycastle.crypto.InvalidCipherTextException; 7 | import org.spongycastle.crypto.engines.AESEngine; 8 | import org.spongycastle.crypto.modes.CBCBlockCipher; 9 | import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; 10 | import org.spongycastle.crypto.params.KeyParameter; 11 | import org.spongycastle.crypto.params.ParametersWithIV; 12 | 13 | import java.security.GeneralSecurityException; 14 | 15 | import javax.crypto.SecretKey; 16 | 17 | /** 18 | * Copy of {@link org.hive2hive.core.security.BCStrongAESEncryption}, but using spongy castle 19 | * 20 | * @author Nico 21 | */ 22 | public class SCStrongAESEncryption implements IStrongAESEncryption { 23 | 24 | @Override 25 | public byte[] encryptStrongAES(byte[] data, SecretKey key, byte[] initVector) throws GeneralSecurityException { 26 | try { 27 | return processAESCipher(true, data, key, initVector); 28 | } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) { 29 | throw new GeneralSecurityException("Cannot encrypt the data with AES 256bit", e); 30 | } 31 | } 32 | 33 | @Override 34 | public byte[] decryptStrongAES(byte[] data, SecretKey key, byte[] initVector) throws GeneralSecurityException { 35 | try { 36 | return processAESCipher(false, data, key, initVector); 37 | } catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) { 38 | throw new GeneralSecurityException("Cannot decrypt the data with AES 256bit", e); 39 | } 40 | } 41 | 42 | private static byte[] processAESCipher(boolean encrypt, byte[] data, SecretKey key, byte[] initVector) 43 | throws DataLengthException, IllegalStateException, InvalidCipherTextException { 44 | // seat up engine, block cipher mode and padding 45 | AESEngine aesEngine = new AESEngine(); 46 | CBCBlockCipher cbc = new CBCBlockCipher(aesEngine); 47 | PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(cbc); 48 | 49 | // apply parameters 50 | CipherParameters parameters = new ParametersWithIV(new KeyParameter(key.getEncoded()), initVector); 51 | cipher.init(encrypt, parameters); 52 | 53 | // process ciphering 54 | byte[] output = new byte[cipher.getOutputSize(data.length)]; 55 | 56 | int bytesProcessed1 = cipher.processBytes(data, 0, data.length, output, 0); 57 | int bytesProcessed2 = cipher.doFinal(output, bytesProcessed1); 58 | byte[] result = new byte[bytesProcessed1 + bytesProcessed2]; 59 | System.arraycopy(output, 0, result, 0, result.length); 60 | return result; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/java/org/hive2hive/mobile/security/SpongyCastleEncryption.java: -------------------------------------------------------------------------------- 1 | package org.hive2hive.mobile.security; 2 | 3 | import org.hive2hive.core.security.H2HDefaultEncryption; 4 | import org.hive2hive.core.serializer.IH2HSerialize; 5 | 6 | import java.security.Security; 7 | 8 | /** 9 | * @author Nico 10 | */ 11 | public class SpongyCastleEncryption extends H2HDefaultEncryption { 12 | 13 | 14 | public SpongyCastleEncryption(IH2HSerialize serializer) { 15 | super(serializer, SCSecurityClassProvider.SECURITY_PROVIDER, new SCStrongAESEncryption()); 16 | 17 | // install the SC provider instead of the BC provider 18 | if (Security.getProvider(SCSecurityClassProvider.SECURITY_PROVIDER) == null) { 19 | Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/animator/slide_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/animator/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/animator/slide_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/animator/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-hdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-hdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-hdpi/ic_action_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-hdpi/ic_action_upload.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-mdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-mdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-mdpi/ic_action_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-mdpi/ic_action_upload.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xhdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xhdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xhdpi/ic_action_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xhdpi/ic_action_upload.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xxhdpi/ic_action_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xxhdpi/ic_action_upload.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/h2h_logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/h2h_logo_large.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_archive_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_archive_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_archive_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_archive_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_audio_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_audio_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_audio_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_audio_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_blank_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_blank_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_blank_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_blank_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_calendar_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_calendar_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_calendar_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_calendar_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_cloud_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_cloud_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_code_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_code_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_code_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_code_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_download_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_download_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_folder.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_folder_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_folder_loading.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_folder_shared.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_folder_shared.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_folder_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_folder_upload.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_image_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_image_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_image_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_image_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_loading_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_loading_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_powerpoint_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_powerpoint_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_powerpoint_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_powerpoint_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_spreadsheet_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_spreadsheet_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_spreadsheet_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_spreadsheet_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_text_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_text_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_text_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_text_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_upload_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_upload_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_video_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_video_ex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/ic_video_inex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/ic_video_inex.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/drawable/login_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hive2Hive/Android/68599adabe7284df58be55e03688a4f999c1b510/org.hive2hive.mobile/app/src/main/res/drawable/login_lock.png -------------------------------------------------------------------------------- /org.hive2hive.mobile/app/src/main/res/layout/activity_connect.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 27 | 28 |