├── .classpath
├── .gitignore
├── .project
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── icon512.xcf
├── libs
├── CACHEWORD_LICENSE
├── IOCipher-v0.3-2-g926b982.jar
├── SQLCIPHER_LICENSE
├── android-support-v4.jar
├── android-support-v4.jar.properties
├── armeabi-v7a
│ ├── libiocipher.so
│ ├── libsqlcipher_android.so
│ ├── libstlport_shared.so
│ └── sqlfscat
├── armeabi
│ ├── libiocipher.so
│ ├── libsqlcipher_android.so
│ ├── libstlport_shared.so
│ └── sqlfscat
├── cachewordlib-v0.1-3-g71cc426-debug.jar
├── jcodec.jar
├── svg-android.jar
└── x86
│ ├── libiocipher.so
│ ├── libsqlcipher_android.so
│ ├── libstlport_shared.so
│ └── sqlfscat
├── project.properties
├── res
├── drawable
│ ├── audio.png
│ ├── folder.png
│ ├── ic_action_camera.png
│ ├── ic_action_secure.png
│ ├── ic_action_switch_camera.png
│ ├── ic_action_switch_video.png
│ ├── ic_action_video.png
│ ├── ic_launcher.png
│ ├── jpeg.png
│ ├── mp4.png
│ ├── splash.png
│ ├── text.png
│ └── videoclip.png
├── layout-land
│ ├── activity_gallery.xml
│ └── base_camera.xml
├── layout
│ ├── activity_gallery.xml
│ ├── activity_lock_screen.xml
│ ├── base_camera.xml
│ ├── gallery_gridsq.xml
│ ├── pzsimageview.xml
│ └── videoview.xml
├── menu
│ └── main.xml
└── values
│ ├── colors.xml
│ └── strings.xml
├── src
└── info
│ └── guardianproject
│ └── iocipher
│ └── camera
│ ├── AudioRecorderActivity.java
│ ├── CameraBaseActivity.java
│ ├── GalleryActivity.java
│ ├── LockScreenActivity.java
│ ├── MediaConstants.java
│ ├── StillCameraActivity.java
│ ├── StorageManager.java
│ ├── VideoCameraActivity.java
│ ├── encoders
│ ├── AACHelper.java
│ ├── ImageToH264MP4Encoder.java
│ ├── ImageToMJPEGMOVMuxer.java
│ ├── ImageToVP8Encoder.java
│ └── YUVtoWebmMuxer.java
│ ├── io
│ ├── IOCipherContentProvider.java
│ └── IOCipherFileChannelWrapper.java
│ └── viewer
│ ├── ImageViewerActivity.java
│ ├── MjpegInputStream.java
│ ├── MjpegView.java
│ ├── MjpegViewerActivity.java
│ ├── PZSImageView.java
│ ├── StreamOverHttp.java
│ └── VideoViewerActivity.java
└── web_hi_res_512.png
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | gen/
3 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | CameraCipherLibrary
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
105 |
106 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This file contains the license for IOCipher Camera Library "CipherCam".
2 |
3 | For more information about IOCipher, see https://guardianproject.info/code/iocipher
4 | and this sample library: https://github.com/n8fr8/IOCipherCameraExample
5 |
6 | If you got this file as a part of a larger bundle, there may be other
7 | license terms that you should be aware of.
8 | ===============================================================================
9 | IOCipher Camera Sample is distributed under this license (aka the 3-clause BSD license)
10 |
11 | Copyright (C) 2014-2014 Nathan Freitas
12 |
13 | Redistribution and use in source and binary forms, with or without
14 | modification, are permitted provided that the following conditions are
15 | met:
16 |
17 | * Redistributions of source code must retain the above copyright
18 | notice, this list of conditions and the following disclaimer.
19 |
20 | * Redistributions in binary form must reproduce the above
21 | copyright notice, this list of conditions and the following disclaimer
22 | in the documentation and/or other materials provided with the
23 | distribution.
24 |
25 | * Neither the names of the copyright owners nor the names of its
26 | contributors may be used to endorse or promote products derived from
27 | this software without specific prior written permission.
28 |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 |
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Camera Cipher Library
3 | ================
4 |
5 | This is an library and example app for the IOCipher framework, which provides encrypted virtual disks for Android.
6 |
7 | This example demonstrates how to capture a still photo (as an in memory byte[] array) directly from the Camera sensor, store that as an encrypted JPEG file directly inside of IOCipher, and then later share that file as a in-memory byte[] or a stream directly from a ContentProvider. It also handles recording video with audio in the same manner, using the MJPEG MP4 Format and Raw PCM audio.
8 |
9 | The example also provides two basic media viewer activities for photos and video. This provides an in-app mechanism for securely displaying media stored in IOCipher, without having to export the media to a file or share it with another app. These are very simple activities for now, but demonstrate the basic premise of how to extract and render media data from IOCipher.
10 |
11 | This example also uses the CacheWord library at https://github.com/guardianproject/cacheword to manage keys and passphrases.
12 |
13 | Finally, all media processing is done using the latest code from JCodec at http://jcodec.org/
14 |
15 | Please report bugs here:
16 | https://dev.guardianproject.info/projects/iocipher/issues
17 |
18 | Find out all about IOCipher here:
19 | https://guardianproject.info/code/iocipher/
20 |
21 | We'd like to hear from you if you have included IOCipher in your app!
22 | support@guardianproject.info
23 |
24 | MjpegView code originally sourced from various samples and threads. More information at:
25 | https://github.com/elleryq/MjpegSample
26 |
27 | SVG-Android library available at:
28 | https://github.com/pents90/svg-android
29 |
30 | Some icons courtesy of: http://www.pelfusion.com/
31 | and http://www.iconarchive.com/show/flat-folder-icons-by-pelfusion/Empty-Folder-icon.html
32 | (free for non-commercial use)
33 |
--------------------------------------------------------------------------------
/icon512.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/icon512.xcf
--------------------------------------------------------------------------------
/libs/CACHEWORD_LICENSE:
--------------------------------------------------------------------------------
1 | This file contains the license for CacheWord.
2 |
3 | For more information about CacheWord, see https://guardianproject.info/
4 |
5 | If you got this file as a part of a larger bundle, there may be other
6 | license terms that you should be aware of.
7 | ===============================================================================
8 | CacheWord is distributed under this license (aka the 3-clause BSD license)
9 |
10 | Copyright (C) 2013-2014 Abel Luck
11 |
12 | Redistribution and use in source and binary forms, with or without
13 | modification, are permitted provided that the following conditions are
14 | met:
15 |
16 | * Redistributions of source code must retain the above copyright
17 | notice, this list of conditions and the following disclaimer.
18 |
19 | * Redistributions in binary form must reproduce the above
20 | copyright notice, this list of conditions and the following disclaimer
21 | in the documentation and/or other materials provided with the
22 | distribution.
23 |
24 | * Neither the names of the copyright owners nor the names of its
25 | contributors may be used to endorse or promote products derived from
26 | this software without specific prior written permission.
27 |
28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 |
40 |
--------------------------------------------------------------------------------
/libs/IOCipher-v0.3-2-g926b982.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/IOCipher-v0.3-2-g926b982.jar
--------------------------------------------------------------------------------
/libs/SQLCIPHER_LICENSE:
--------------------------------------------------------------------------------
1 | == SQLite Cipher ==
2 |
3 | SQLite Cipher is an SQLite extension that provides transparent 256 bit AES encryption of database files.
4 | Pages are encrypted before being written to disk and are decrypted when read back.
5 |
6 | Encryption is provided by the OpenSSL crypto library.
7 |
8 | SQLite Cipher was initially developed by Stephen Lombardo at Zetetic LLC (sjlombardo@zetetic.net) to provide
9 | the encrypted database layer for Strip, an iPhone data vault and password manager ( http://www.zetetic.net/products/strip ).
10 |
11 | The official SQLCipher software site can be found at http://www.zetetic.net/software/sqlcipher
12 |
13 | Issues or support questions on using SQLCipher should be entered into the GitHub Issue tracker:
14 |
15 | http://github.com/sjlombardo/sqlcipher/issues
16 |
17 | Please DO NOT post issues, support questions, or other problems to blog posts about SQLCipher as we do not monitor them frequently.
18 |
19 | Copyright (c) 2010 Zetetic LLC
20 | All rights reserved.
21 |
22 | Redistribution and use in source and binary forms, with or without
23 | modification, are permitted provided that the following conditions are met:
24 | * Redistributions of source code must retain the above copyright
25 | notice, this list of conditions and the following disclaimer.
26 | * Redistributions in binary form must reproduce the above copyright
27 | notice, this list of conditions and the following disclaimer in the
28 | documentation and/or other materials provided with the distribution.
29 | * Neither the name of the ZETETIC LLC nor the
30 | names of its contributors may be used to endorse or promote products
31 | derived from this software without specific prior written permission.
32 |
33 | THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
34 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
35 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36 | DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
37 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
40 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 |
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/android-support-v4.jar.properties:
--------------------------------------------------------------------------------
1 | src=android-support-v4.jar
2 | doc=/opt/android-sdk/docs/reference
3 | # The String value of 'doc' is handed straight to `new java.io.File()` so it
4 | # is not possible to use variables. Therefore, change "/opt/android-sdk" to
5 | # the path to your Android SDK, i.e. the value of $ANDROID_HOME or ${sdk.dir}
6 | #
7 | # Here's the relevant source:
8 | # https://android-review.googlesource.com/#/c/35702/3/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java
9 |
--------------------------------------------------------------------------------
/libs/armeabi-v7a/libiocipher.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi-v7a/libiocipher.so
--------------------------------------------------------------------------------
/libs/armeabi-v7a/libsqlcipher_android.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi-v7a/libsqlcipher_android.so
--------------------------------------------------------------------------------
/libs/armeabi-v7a/libstlport_shared.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi-v7a/libstlport_shared.so
--------------------------------------------------------------------------------
/libs/armeabi-v7a/sqlfscat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi-v7a/sqlfscat
--------------------------------------------------------------------------------
/libs/armeabi/libiocipher.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi/libiocipher.so
--------------------------------------------------------------------------------
/libs/armeabi/libsqlcipher_android.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi/libsqlcipher_android.so
--------------------------------------------------------------------------------
/libs/armeabi/libstlport_shared.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi/libstlport_shared.so
--------------------------------------------------------------------------------
/libs/armeabi/sqlfscat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/armeabi/sqlfscat
--------------------------------------------------------------------------------
/libs/cachewordlib-v0.1-3-g71cc426-debug.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/cachewordlib-v0.1-3-g71cc426-debug.jar
--------------------------------------------------------------------------------
/libs/jcodec.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/jcodec.jar
--------------------------------------------------------------------------------
/libs/svg-android.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/svg-android.jar
--------------------------------------------------------------------------------
/libs/x86/libiocipher.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/x86/libiocipher.so
--------------------------------------------------------------------------------
/libs/x86/libsqlcipher_android.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/x86/libsqlcipher_android.so
--------------------------------------------------------------------------------
/libs/x86/libstlport_shared.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/x86/libstlport_shared.so
--------------------------------------------------------------------------------
/libs/x86/sqlfscat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/libs/x86/sqlfscat
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 |
12 | # Project target.
13 | target=android-19
14 | android.library=true
15 |
--------------------------------------------------------------------------------
/res/drawable/audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/audio.png
--------------------------------------------------------------------------------
/res/drawable/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/folder.png
--------------------------------------------------------------------------------
/res/drawable/ic_action_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/ic_action_camera.png
--------------------------------------------------------------------------------
/res/drawable/ic_action_secure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/ic_action_secure.png
--------------------------------------------------------------------------------
/res/drawable/ic_action_switch_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/ic_action_switch_camera.png
--------------------------------------------------------------------------------
/res/drawable/ic_action_switch_video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/ic_action_switch_video.png
--------------------------------------------------------------------------------
/res/drawable/ic_action_video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/ic_action_video.png
--------------------------------------------------------------------------------
/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable/jpeg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/jpeg.png
--------------------------------------------------------------------------------
/res/drawable/mp4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/mp4.png
--------------------------------------------------------------------------------
/res/drawable/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/splash.png
--------------------------------------------------------------------------------
/res/drawable/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/text.png
--------------------------------------------------------------------------------
/res/drawable/videoclip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardianproject/CamCipher/fedaa54cd5d59b127df95270d0842214df74cd70/res/drawable/videoclip.png
--------------------------------------------------------------------------------
/res/layout-land/activity_gallery.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/layout-land/base_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
14 |
15 |
16 |
23 |
24 |
30 |
31 |
38 |
39 |
40 |
41 |
42 |
49 |
50 |
58 |
59 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/res/layout/activity_gallery.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/layout/activity_lock_screen.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
17 |
18 |
19 |
22 |
23 |
28 |
29 |
33 |
34 |
41 |
42 |
54 |
55 |
56 |
61 |
62 |
76 |
77 |
78 |
79 |
88 |
89 |
90 |
96 |
97 |
102 |
103 |
116 |
117 |
118 |
119 |
120 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/res/layout/base_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
15 |
16 |
17 |
25 |
26 |
32 |
45 |
46 |
47 |
48 |
49 |
57 |
58 |
64 |
65 |
66 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/res/layout/gallery_gridsq.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 | />
20 |
21 |
--------------------------------------------------------------------------------
/res/layout/pzsimageview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/res/layout/videoview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #AAFFFFFF
4 |
5 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CipherCam
5 |
6 |
7 | Open
8 | Locked
9 | Create passphrase
10 | Enter passphrase
11 | Enter new passphrase
12 | Confirm new passphrase
13 | Passphrases did not match, please try again
14 |
15 | Your passphrase is too short
16 |
17 | Install Orbot?
18 |
19 | market://search?q=pname:org.torproject.android
20 |
21 | You must have Orbot installed and activated to proxy traffic through it. Would you like to install it from Google Play?
22 |
23 | Yes
24 |
25 | No
26 |
27 | Always
28 |
29 | Start Orbot?
30 |
31 | Orbot doesn\'t appear to be running. Would you like to start it up and connect to Tor?
32 |
33 |
--------------------------------------------------------------------------------
/src/info/guardianproject/iocipher/camera/AudioRecorderActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This file contains code from the IOCipher Camera Library "CipherCam".
4 | *
5 | * For more information about IOCipher, see https://guardianproject.info/code/iocipher
6 | * and this sample library: https://github.com/n8fr8/IOCipherCameraExample
7 | *
8 | * IOCipher Camera Sample is distributed under this license (aka the 3-clause BSD license)
9 | *
10 | * @author n8fr8
11 | *
12 | */
13 |
14 | package info.guardianproject.iocipher.camera;
15 |
16 | import info.guardianproject.iocipher.File;
17 | import info.guardianproject.iocipher.camera.encoders.AACHelper;
18 |
19 | import java.io.BufferedOutputStream;
20 | import java.io.IOException;
21 | import java.io.OutputStream;
22 |
23 | import android.app.Activity;
24 | import android.media.AudioFormat;
25 | import android.media.AudioRecord;
26 | import android.media.MediaRecorder;
27 | import android.os.Bundle;
28 | import android.os.Handler;
29 | import android.os.Message;
30 | import android.provider.MediaStore;
31 | import android.util.Log;
32 | import android.view.View;
33 | import android.view.View.OnClickListener;
34 | import android.widget.Toast;
35 |
36 | public class AudioRecorderActivity extends Activity implements OnClickListener {
37 |
38 | private final static String LOG = "AudioRecorder";
39 |
40 | private String mFileBasePath = null;
41 | private boolean mIsRecording = false;
42 |
43 | private AACHelper aac;
44 | private boolean useAAC = false;
45 | private byte[] audioData;
46 | private AudioRecord audioRecord;
47 |
48 | private OutputStream outputStreamAudio;
49 | private info.guardianproject.iocipher.File fileAudio;
50 |
51 | private boolean isRequest = false;
52 |
53 | @Override
54 | public void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | mFileBasePath = getIntent().getStringExtra("basepath");
57 |
58 | isRequest = getIntent().getAction() != null && getIntent().getAction().equals(MediaStore.ACTION_VIDEO_CAPTURE);
59 |
60 | }
61 |
62 | final Handler handler = new Handler();
63 | Runnable mLongPressed = new Runnable() {
64 | public void run() {
65 | Log.i("", "Long press!");
66 |
67 | startRecording();
68 |
69 | }
70 | };
71 |
72 | @Override
73 | public void onClick(View view) {
74 |
75 | if (!mIsRecording)
76 | {
77 |
78 | startRecording();
79 |
80 |
81 | }
82 | else
83 | {
84 | stopRecording ();
85 | }
86 |
87 | }
88 |
89 | private void startRecording ()
90 | {
91 | String fileName = "audio" + new java.util.Date().getTime();
92 | info.guardianproject.iocipher.File fileOut = new info.guardianproject.iocipher.File(mFileBasePath,fileName);
93 |
94 | try {
95 | mIsRecording = true;
96 |
97 | if (useAAC)
98 | initAudio(fileOut.getAbsolutePath()+".aac");
99 | else
100 | initAudio(fileOut.getAbsolutePath()+".pcm");
101 |
102 |
103 | startAudioRecording();
104 |
105 |
106 | } catch (Exception e) {
107 | Log.d("Video","error starting video",e);
108 | Toast.makeText(this, "Error init'ing video: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
109 | finish();
110 | }
111 | }
112 |
113 | private void stopRecording ()
114 | {
115 | h.sendEmptyMessageDelayed(1, 2000);
116 | }
117 |
118 |
119 | Handler h = new Handler ()
120 | {
121 |
122 | @Override
123 | public void handleMessage(Message msg) {
124 | // TODO Auto-generated method stub
125 | super.handleMessage(msg);
126 |
127 | if (msg.what == 0)
128 | {
129 |
130 | }
131 | else if (msg.what == 1)
132 | {
133 | mIsRecording = false; //stop recording
134 |
135 | if (aac != null)
136 | aac.stopRecording();
137 | }
138 | }
139 |
140 | };
141 |
142 | private void initAudio(final String audioPath) throws Exception {
143 |
144 | fileAudio = new File(audioPath);
145 |
146 | outputStreamAudio = new BufferedOutputStream(new info.guardianproject.iocipher.FileOutputStream(fileAudio),8192*8);
147 |
148 | if (useAAC)
149 | {
150 | aac = new AACHelper();
151 | aac.setEncoder(MediaConstants.sAudioSampleRate, MediaConstants.sAudioChannels, MediaConstants.sAudioBitRate);
152 | }
153 | else
154 | {
155 |
156 | int minBufferSize = AudioRecord.getMinBufferSize(MediaConstants.sAudioSampleRate,
157 | MediaConstants.sChannelConfigIn,
158 | AudioFormat.ENCODING_PCM_16BIT)*8;
159 |
160 | audioData = new byte[minBufferSize];
161 |
162 | int audioSource = MediaRecorder.AudioSource.CAMCORDER;
163 | // audioSource = MediaRecorder.AudioSource.MIC;
164 |
165 | audioRecord = new AudioRecord(audioSource,
166 | MediaConstants.sAudioSampleRate,
167 | MediaConstants.sChannelConfigIn,
168 | AudioFormat.ENCODING_PCM_16BIT,
169 | minBufferSize);
170 | }
171 | }
172 |
173 | private void startAudioRecording ()
174 | {
175 |
176 |
177 | Thread thread = new Thread ()
178 | {
179 |
180 | public void run ()
181 | {
182 |
183 | if (useAAC)
184 | {
185 | try {
186 | aac.startRecording(outputStreamAudio);
187 | } catch (IOException e) {
188 | // TODO Auto-generated catch block
189 | e.printStackTrace();
190 | }
191 | }
192 | else
193 | {
194 | audioRecord.startRecording();
195 |
196 | while(mIsRecording){
197 | int audioDataBytes = audioRecord.read(audioData, 0, audioData.length);
198 | if (AudioRecord.ERROR_INVALID_OPERATION != audioDataBytes
199 | && outputStreamAudio != null) {
200 | try {
201 | outputStreamAudio.write(audioData,0,audioDataBytes);
202 |
203 | //muxer.addAudio(ByteBuffer.wrap(audioData, 0, audioData.length));
204 | } catch (IOException e) {
205 | e.printStackTrace();
206 | }
207 | }
208 | }
209 |
210 | audioRecord.stop();
211 | audioRecord.release();
212 | audioRecord = null;
213 |
214 | try {
215 | outputStreamAudio.flush();
216 | outputStreamAudio.close();
217 | } catch (IOException e) {
218 | // TODO Auto-generated catch block
219 | e.printStackTrace();
220 | }
221 | }
222 |
223 |
224 | }
225 | };
226 |
227 | thread.start();
228 |
229 | }
230 |
231 |
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/src/info/guardianproject/iocipher/camera/CameraBaseActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This file contains code from the IOCipher Camera Library "CipherCam".
4 | *
5 | * For more information about IOCipher, see https://guardianproject.info/code/iocipher
6 | * and this sample library: https://github.com/n8fr8/IOCipherCameraExample
7 | *
8 | * IOCipher Camera Sample is distributed under this license (aka the 3-clause BSD license)
9 | *
10 | * @author n8fr8
11 | *
12 | */
13 |
14 | package info.guardianproject.iocipher.camera;
15 |
16 | import java.io.IOException;
17 | import java.util.List;
18 |
19 | import android.annotation.SuppressLint;
20 | import android.app.Activity;
21 | import android.content.pm.PackageManager;
22 | import android.content.res.Configuration;
23 | import android.graphics.Bitmap;
24 | import android.graphics.Canvas;
25 | import android.graphics.ImageFormat;
26 | import android.graphics.Picture;
27 | import android.graphics.Rect;
28 | import android.hardware.Camera;
29 | import android.hardware.Camera.CameraInfo;
30 | import android.hardware.Camera.Parameters;
31 | import android.hardware.Camera.PictureCallback;
32 | import android.hardware.Camera.PreviewCallback;
33 | import android.hardware.Camera.Size;
34 | import android.os.Build;
35 | import android.os.Bundle;
36 | import android.util.Log;
37 | import android.view.MotionEvent;
38 | import android.view.Surface;
39 | import android.view.SurfaceHolder;
40 | import android.view.SurfaceView;
41 | import android.view.View;
42 | import android.view.View.OnClickListener;
43 | import android.view.View.OnTouchListener;
44 | import android.widget.Button;
45 | import android.widget.ImageView;
46 | import android.widget.TextView;
47 |
48 | import com.larvalabs.svgandroid.SVG;
49 | import com.larvalabs.svgandroid.SVGParser;
50 |
51 | public abstract class CameraBaseActivity extends Activity implements OnClickListener, OnTouchListener, SurfaceHolder.Callback, PictureCallback, PreviewCallback {
52 |
53 | Button button;
54 | TextView progress;
55 |
56 | Button buttonSelfie;
57 | boolean mIsSelfie = false;
58 |
59 | SurfaceView view;
60 | SurfaceHolder holder;
61 | Camera camera;
62 | CameraInfo cameraInfo;
63 |
64 | View overlayView;
65 |
66 | protected boolean mPreviewing;
67 |
68 | private final static String LOG = "CipherCam";
69 |
70 | protected int mRotation = 0;
71 |
72 | private int mPreviewWidth = -1;
73 | private int mPreviewHeight = -1;
74 |
75 | @SuppressWarnings("deprecation")
76 | @Override
77 | public void onCreate(Bundle savedInstanceState) {
78 | super.onCreate(savedInstanceState);
79 |
80 | View decorView = getWindow().getDecorView();
81 | int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
82 | decorView.setSystemUiVisibility(uiOptions);
83 |
84 | setContentView(getLayout());
85 |
86 | mIsSelfie = getIntent().getBooleanExtra("selfie", false);
87 |
88 | button = (Button) findViewById(R.id.surface_grabber_button);
89 | button.setOnClickListener(this);
90 |
91 | buttonSelfie = (Button)findViewById(R.id.selfie_button);
92 | buttonSelfie.setOnClickListener(new OnClickListener()
93 | {
94 |
95 | @Override
96 | public void onClick(View v) {
97 | toggleCamera();
98 |
99 | }
100 |
101 | });
102 |
103 | progress = (TextView) findViewById(R.id.surface_grabber_progress);
104 | view = (SurfaceView) findViewById(R.id.surface_grabber_holder);
105 | overlayView = findViewById(R.id.overlay_view);
106 |
107 |
108 | holder = view.getHolder();
109 | holder.addCallback(this);
110 | holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
111 |
112 | view.setOnClickListener(this);
113 |
114 | view.setOnTouchListener(this);
115 |
116 | }
117 |
118 | /*
119 | private void setOverlayImage (String path)
120 | {
121 | try
122 | {
123 |
124 | Bitmap bitmap = Bitmap.createBitmap(overlayView.getWidth(),overlayView.getHeight(), Bitmap.Config.ARGB_8888);
125 | Canvas canvas = new Canvas(bitmap);
126 |
127 | SVG svg = SVGParser.getSVGFromAsset(getAssets(), path);
128 |
129 | Rect rBounds = new Rect(0,0,overlayView.getWidth(),overlayView.getHeight());
130 | Picture p = svg.getPicture();
131 | canvas.drawPicture(p, rBounds);
132 |
133 | overlayView.setImageBitmap( bitmap);
134 | }
135 | catch(IOException ex)
136 | {
137 | Log.e("BaseCamera","error rendering overlay",ex);
138 | return;
139 | }
140 |
141 | }*/
142 |
143 |
144 | protected int getLayout()
145 | {
146 | return R.layout.base_camera;
147 | }
148 |
149 |
150 | protected int getCameraDirection() {
151 | if (mIsSelfie)
152 | return CameraInfo.CAMERA_FACING_FRONT;
153 | else
154 | return CameraInfo.CAMERA_FACING_BACK;
155 | }
156 |
157 |
158 | /**
159 | * Whether or not we can default to "other" direction if our preferred facing camera can't be opened
160 | * @return true to try camera facing other way, false otherwise
161 | */
162 | protected boolean canUseOtherDirection()
163 | {
164 | return false;
165 | }
166 |
167 |
168 | @Override
169 | public void onResume() {
170 | super.onResume();
171 |
172 | initCamera();
173 | }
174 |
175 | protected void initCamera()
176 | {
177 |
178 | if (!tryCreateCamera(getCameraDirection()))
179 | {
180 | if (!canUseOtherDirection() || !tryCreateCamera(getOtherDirection(getCameraDirection())))
181 | {
182 | finish();
183 | return;
184 | }
185 | }
186 |
187 | if(camera == null)
188 | finish();
189 |
190 | }
191 |
192 | private int getOtherDirection(int facing)
193 | {
194 | return (facing == CameraInfo.CAMERA_FACING_BACK) ? CameraInfo.CAMERA_FACING_FRONT : CameraInfo.CAMERA_FACING_BACK;
195 | }
196 |
197 | @SuppressLint("NewApi")
198 | private boolean tryCreateCamera(int facing)
199 | {
200 | Camera.CameraInfo info = new Camera.CameraInfo();
201 | for (int nCam = 0; nCam < Camera.getNumberOfCameras(); nCam++)
202 | {
203 | Camera.getCameraInfo(nCam, info);
204 | if (info.facing == facing)
205 | {
206 | camera = Camera.open(nCam);
207 | cameraInfo = info;
208 |
209 | Camera.Parameters params = camera.getParameters();
210 | params.setPictureFormat(ImageFormat.JPEG);
211 |
212 | mRotation = setCameraDisplayOrientation(this,nCam,camera);
213 |
214 | List supportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
215 | List supportedPictureSize = camera.getParameters().getSupportedPictureSizes();
216 |
217 | int previewQuality = 5;
218 |
219 | if (mPreviewWidth == -1)
220 | mPreviewWidth = supportedPreviewSizes.get(previewQuality).width;
221 |
222 | if (mPreviewHeight == -1)
223 | mPreviewHeight = supportedPreviewSizes.get(previewQuality).height;
224 |
225 | params.setPreviewSize(mPreviewWidth, mPreviewHeight);
226 |
227 | params.setPictureSize(supportedPictureSize.get(1).width, supportedPictureSize.get(1).height);
228 |
229 | if (this.getCameraDirection() == CameraInfo.CAMERA_FACING_BACK)
230 | {
231 | if (getPackageManager().hasSystemFeature(
232 | PackageManager.FEATURE_CAMERA_AUTOFOCUS))
233 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
234 | params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
235 | }
236 |
237 |
238 | if (mRotation > 0)
239 | params.setRotation(mRotation);
240 |
241 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
242 | if (params.isVideoStabilizationSupported())
243 | params.setVideoStabilization(true);
244 |
245 | }
246 |
247 | }
248 | else
249 | {
250 | if (mRotation > 0)
251 | params.setRotation(360-mRotation);
252 | }
253 |
254 | camera.setParameters(params);
255 |
256 | /*
257 | for (int i = 0; i < BUFFER_COUNT; i++) {
258 | byte[] buffer = new byte[mPreviewWidth * mPreviewHeight *
259 | ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8];
260 | camera.addCallbackBuffer(buffer);
261 |
262 | }
263 |
264 | // camera.setPreviewCallback(this);
265 | camera.setPreviewCallbackWithBuffer(this);
266 | */
267 | camera.setPreviewCallback(this);
268 |
269 | if (holder != null)
270 | try {
271 | camera.setPreviewDisplay(holder);
272 | camera.startPreview();
273 | } catch (IOException e) {
274 | // TODO Auto-generated catch block
275 | e.printStackTrace();
276 | }
277 | //start video
278 |
279 | return true;
280 | }
281 | }
282 | return false;
283 | }
284 |
285 |
286 | @Override
287 | public void onPause() {
288 | releaseCamera();
289 |
290 | super.onPause();
291 | }
292 |
293 | protected void releaseCamera ()
294 | {
295 | try
296 | {
297 | // release the camera immediately on pause event
298 | camera.stopPreview();
299 | camera.setPreviewCallback(null);
300 | camera.release();
301 | camera = null;
302 |
303 | }
304 | catch(Exception e)
305 | {
306 | e.printStackTrace();
307 | }
308 |
309 | }
310 |
311 | @Override
312 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
313 |
314 | if (camera != null)
315 | {
316 | camera.startPreview();
317 | mPreviewing = true;
318 | }
319 | }
320 |
321 | protected Size choosePictureSize(List localSizes)
322 | {
323 | Size size = null;
324 |
325 | for(Size sz : localSizes) {
326 | if(sz.width > 640 && sz.width <= 1024)
327 | size = sz;
328 |
329 | if(size != null)
330 | break;
331 | }
332 |
333 | if(size == null)
334 | size = localSizes.get(localSizes.size() - 1);
335 | return size;
336 | }
337 |
338 | @Override
339 | public void surfaceCreated(SurfaceHolder holder) {
340 | try {
341 |
342 | this.holder = holder;
343 |
344 | if (camera != null)
345 | camera.setPreviewDisplay(holder);
346 |
347 | } catch(IOException e) {
348 | Log.e(LOG, e.toString());
349 | }
350 | }
351 |
352 | @Override
353 | public void surfaceDestroyed(SurfaceHolder holder) {}
354 |
355 | @Override
356 | public void onClick(View view) {
357 | if(mPreviewing) {
358 | mPreviewing = false;
359 | camera.takePicture(null, null, this);
360 | }
361 | }
362 |
363 |
364 | @Override
365 | public boolean onTouch(View v, MotionEvent event) {
366 |
367 | return false;
368 | }
369 |
370 |
371 |
372 | private void toggleCamera ()
373 | {
374 | mIsSelfie = !mIsSelfie;
375 | releaseCamera();
376 | initCamera();
377 | }
378 |
379 | @Override
380 | public void onPictureTaken(byte[] data, Camera camera) {
381 | //do nothing by default
382 | }
383 |
384 |
385 | @Override
386 | public void onPreviewFrame(byte[] data, Camera camera) {
387 |
388 | //do nothing by default
389 | }
390 |
391 |
392 | protected void resumePreview()
393 | {
394 | if (!mPreviewing)
395 | {
396 | camera.startPreview();
397 | mPreviewing = true;
398 | }
399 | }
400 |
401 |
402 | public static int setCameraDisplayOrientation(Activity activity,
403 | int cameraId, android.hardware.Camera camera) {
404 | android.hardware.Camera.CameraInfo info =
405 | new android.hardware.Camera.CameraInfo();
406 | android.hardware.Camera.getCameraInfo(cameraId, info);
407 | int rotation = activity.getWindowManager().getDefaultDisplay()
408 | .getRotation();
409 | int degrees = 0;
410 | switch (rotation) {
411 | case Surface.ROTATION_0: degrees = 0; break;
412 | case Surface.ROTATION_90: degrees = 90; break;
413 | case Surface.ROTATION_180: degrees = 180; break;
414 | case Surface.ROTATION_270: degrees = 270; break;
415 | }
416 |
417 | int result;
418 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
419 | result = (info.orientation + degrees) % 360;
420 | result = (360 - result) % 360; // compensate the mirror
421 | } else { // back-facing
422 | result = (info.orientation - degrees + 360) % 360;
423 | }
424 | camera.setDisplayOrientation(result);
425 |
426 | return result;
427 | }
428 |
429 |
430 | @Override
431 | public void onConfigurationChanged(Configuration newConfig) {
432 | super.onConfigurationChanged(newConfig);
433 |
434 | releaseCamera ();
435 | initCamera();
436 | }
437 |
438 |
439 | }
--------------------------------------------------------------------------------
/src/info/guardianproject/iocipher/camera/GalleryActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This file contains code from the IOCipher Camera Library "CipherCam".
4 | *
5 | * For more information about IOCipher, see https://guardianproject.info/code/iocipher
6 | * and this sample library: https://github.com/n8fr8/IOCipherCameraExample
7 | *
8 | * IOCipher Camera Sample is distributed under this license (aka the 3-clause BSD license)
9 | *
10 | * @author n8fr8
11 | *
12 | */
13 |
14 | package info.guardianproject.iocipher.camera;
15 |
16 | import info.guardianproject.cacheword.CacheWordHandler;
17 | import info.guardianproject.cacheword.ICacheWordSubscriber;
18 | import info.guardianproject.iocipher.File;
19 | import info.guardianproject.iocipher.FileInputStream;
20 | import info.guardianproject.iocipher.FileOutputStream;
21 | import info.guardianproject.iocipher.camera.io.IOCipherContentProvider;
22 | import info.guardianproject.iocipher.camera.viewer.ImageViewerActivity;
23 | import info.guardianproject.iocipher.camera.viewer.MjpegViewerActivity;
24 |
25 | import java.io.BufferedOutputStream;
26 | import java.io.FileNotFoundException;
27 | import java.io.IOException;
28 | import java.io.InputStream;
29 | import java.io.OutputStream;
30 | import java.util.ArrayList;
31 | import java.util.HashMap;
32 | import java.util.List;
33 |
34 | import android.app.Activity;
35 | import android.app.AlertDialog;
36 | import android.content.ActivityNotFoundException;
37 | import android.content.ContentResolver;
38 | import android.content.DialogInterface;
39 | import android.content.Intent;
40 | import android.content.res.Configuration;
41 | import android.graphics.Bitmap;
42 | import android.graphics.BitmapFactory;
43 | import android.net.Uri;
44 | import android.os.Bundle;
45 | import android.os.Handler;
46 | import android.provider.MediaStore;
47 | import android.util.Log;
48 | import android.view.LayoutInflater;
49 | import android.view.Menu;
50 | import android.view.MenuInflater;
51 | import android.view.MenuItem;
52 | import android.view.View;
53 | import android.view.ViewGroup;
54 | import android.view.WindowManager;
55 | import android.webkit.MimeTypeMap;
56 | import android.widget.AdapterView;
57 | import android.widget.AdapterView.OnItemClickListener;
58 | import android.widget.AdapterView.OnItemLongClickListener;
59 | import android.widget.ArrayAdapter;
60 | import android.widget.GridView;
61 | import android.widget.ImageView;
62 | import android.widget.Toast;
63 |
64 | public class GalleryActivity extends Activity implements ICacheWordSubscriber {
65 | private final static String TAG = "FileBrowser";
66 |
67 | private List item = null;
68 | private List path = null;
69 | private String[] items;
70 | private java.io.File dbFile;
71 | private String root = "/";
72 |
73 | private GridView gridview;
74 | private HashMap mBitCache = new HashMap();
75 | private HashMap mBitLoaders = new HashMap();
76 |
77 | private CacheWordHandler mCacheWord;
78 |
79 | private final static int REQUEST_TAKE_PICTURE = 1000;
80 | private final static int REQUEST_TAKE_VIDEO = 1001;
81 |
82 | private final static String ACTION_SECURE_STILL_IMAGE_CAMERA = "info.guardianproject.action.SECURE_STILL_IMAGE_CAMERA";
83 | private final static String ACTION_SECURE_SECURE_VIDEO_CAMERA = "info.guardianproject.action.SECURE_VIDEO_CAMERA";
84 |
85 | private Handler h = new Handler();//for UI event handling
86 |
87 | private boolean mUseBuiltInLockScreen = false;
88 | private boolean isExternalLaunch = false;
89 |
90 | /** Called when the activity is first created. */
91 | @Override
92 | public void onCreate(Bundle savedInstanceState) {
93 | super.onCreate(savedInstanceState);
94 |
95 | //prevent screenshots
96 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
97 | WindowManager.LayoutParams.FLAG_SECURE);
98 |
99 | setContentView(R.layout.activity_gallery);
100 |
101 | gridview = (GridView) findViewById(R.id.gridview);
102 |
103 | mCacheWord = new CacheWordHandler(this, this);
104 | mCacheWord.connectToService();
105 |
106 | Intent intent = getIntent();
107 | String action = intent.getAction();
108 | String type = intent.getType();
109 | if (Intent.ACTION_SEND.equals(action) && type != null) {
110 | if (intent.hasExtra(Intent.EXTRA_STREAM)) {
111 | Log.i(TAG, "save extra stream URI");
112 | handleSendUri((Uri) intent.getExtras().get(Intent.EXTRA_STREAM));
113 | } else {
114 | Log.i(TAG, "save data");
115 | handleSendUri(intent.getData());
116 | }
117 | }
118 | else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
119 | || ACTION_SECURE_STILL_IMAGE_CAMERA.equals(action)
120 | )
121 | {
122 | //REQUEST_TAKE_PICTURE
123 |
124 | Intent intentCapture = new Intent(this,StillCameraActivity.class);
125 | intentCapture.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
126 | intentCapture.putExtra("basepath", "/");
127 | intentCapture.putExtra("selfie", false);
128 | startActivityForResult(intentCapture, REQUEST_TAKE_PICTURE);
129 | isExternalLaunch = true;
130 | }
131 | else if (MediaStore.ACTION_VIDEO_CAPTURE.equals(action)
132 | || ACTION_SECURE_SECURE_VIDEO_CAMERA.equals(action)
133 | )
134 | {
135 | //REQUEST_TAKE_VIDEO
136 | Intent intentCapture = new Intent(this,VideoCameraActivity.class);
137 | intentCapture.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
138 | intentCapture.putExtra("basepath", "/");
139 | intentCapture.putExtra("selfie", false);
140 | startActivityForResult(intentCapture, REQUEST_TAKE_VIDEO);
141 | isExternalLaunch = true;
142 |
143 | }
144 |
145 | setIntent(null);
146 |
147 | }
148 |
149 |
150 | @Override
151 | public void onConfigurationChanged(Configuration newConfig) {
152 |
153 |
154 | super.onConfigurationChanged(newConfig);
155 |
156 | }
157 |
158 |
159 | protected void onResume() {
160 | super.onResume();
161 |
162 | if (!StorageManager.isStorageMounted())
163 | {
164 | goToLockScreen ();
165 | }
166 | else
167 | {
168 | mCacheWord.reattach();
169 |
170 | }
171 |
172 | }
173 |
174 | @Override
175 | public void onCacheWordLocked() {
176 |
177 | if (StorageManager.isStorageMounted())
178 | {
179 | //if storage is mounted, then we should lock it
180 | boolean unmounted = StorageManager.unmountStorage();
181 | }
182 |
183 | goToLockScreen ();
184 |
185 | }
186 |
187 | @Override
188 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
189 | super.onActivityResult(requestCode, resultCode, data);
190 |
191 | if (requestCode == REQUEST_TAKE_PICTURE)
192 | {
193 | if (resultCode == RESULT_OK)
194 | {
195 | String[] ioCipherFile = data.getExtras().getStringArray(MediaStore.EXTRA_OUTPUT);
196 |
197 | if (ioCipherFile != null && ioCipherFile.length > 0)
198 | {
199 | String sharePath = IOCipherContentProvider.addShare(ioCipherFile[0], IOCipherContentProvider.DEFAULT_AUTHORITY);
200 | Uri uri = Uri.parse(sharePath);
201 | String mimeType = "image/*";
202 | data.setDataAndType(uri, mimeType);
203 | data.putExtra(Intent.EXTRA_STREAM, uri);
204 | data.putExtra(MediaStore.EXTRA_OUTPUT, ioCipherFile);
205 |
206 | setResult(resultCode,data);
207 | }
208 | }
209 |
210 | }
211 | else if (requestCode == REQUEST_TAKE_VIDEO)
212 | {
213 | if (resultCode == RESULT_OK)
214 | {
215 | String[] ioCipherFile = data.getExtras().getStringArray(MediaStore.EXTRA_OUTPUT);
216 |
217 | if (ioCipherFile != null && ioCipherFile.length > 0)
218 | {
219 | String sharePath = IOCipherContentProvider.addShare(ioCipherFile[0], IOCipherContentProvider.DEFAULT_AUTHORITY);
220 | Uri uri = Uri.parse(sharePath);
221 | String mimeType = "video/*";
222 | data.setDataAndType(uri, mimeType);
223 | data.putExtra(Intent.EXTRA_STREAM, uri);
224 | data.putExtra(MediaStore.EXTRA_OUTPUT, ioCipherFile);
225 |
226 | setResult(resultCode,data);
227 | }
228 | }
229 |
230 | }
231 |
232 | if (isExternalLaunch)
233 | finish();
234 | else
235 | getFileList(root);
236 |
237 |
238 | }
239 |
240 | @Override
241 | public void onCacheWordOpened() {
242 |
243 | mCacheWord.setTimeout(0);
244 | //great!
245 | getFileList(root);
246 | }
247 |
248 | @Override
249 | public void onCacheWordUninitialized() {
250 |
251 | goToLockScreen ();
252 |
253 | }
254 |
255 | private void goToLockScreen ()
256 | {
257 | try
258 | {
259 | mCacheWord.disconnectFromService();
260 | }
261 | catch (IllegalArgumentException iae)
262 | {
263 | Log.d(TAG,"error disconnecting from cacheword service",iae);
264 | }
265 |
266 |
267 | if (mUseBuiltInLockScreen && (!isExternalLaunch))
268 | {
269 | Intent intent = new Intent(this,LockScreenActivity.class);
270 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
271 | startActivity(intent);
272 | finish();
273 | }
274 |
275 | }
276 |
277 | @Override
278 | protected void onPause() {
279 | super.onPause();
280 |
281 |
282 | mCacheWord.detach();
283 |
284 | }
285 |
286 | protected void onDestroy() {
287 | super.onDestroy();
288 |
289 | mCacheWord.disconnectFromService();
290 | }
291 |
292 | @Override
293 | public boolean onCreateOptionsMenu(Menu menu) {
294 |
295 | MenuInflater inflater = getMenuInflater();
296 | inflater.inflate(R.menu.main, menu);
297 |
298 | return true;
299 | }
300 |
301 |
302 | @Override
303 | public boolean onOptionsItemSelected(MenuItem item) {
304 |
305 | Intent intent = null;
306 |
307 | int itemId = item.getItemId();
308 |
309 | if (itemId == R.id.menu_camera) {
310 | intent = new Intent(this,StillCameraActivity.class);
311 | intent.putExtra("basepath", "/");
312 | intent.putExtra("selfie", false);
313 | startActivityForResult(intent, 1);
314 | return true;
315 | } else if (itemId == R.id.menu_video) {
316 | intent = new Intent(this,VideoCameraActivity.class);
317 | intent.putExtra("basepath", "/");
318 | intent.putExtra("selfie", false);
319 | startActivityForResult(intent, 1);
320 | return true;
321 | } else if (itemId == R.id.menu_lock) {
322 | if (StorageManager.isStorageMounted())
323 | {
324 | //if storage is mounted, then we should lock it
325 | boolean unmounted = StorageManager.unmountStorage();
326 |
327 | if (!unmounted)
328 | {
329 | Toast.makeText(this, "Storage is busy... cannot lock yet.",Toast.LENGTH_LONG).show();
330 | }
331 | else
332 | {
333 | mCacheWord.lock();
334 | }
335 | }
336 | return true;
337 | }
338 |
339 |
340 | return false;
341 | }
342 |
343 | private void handleSendUri(Uri dataUri) {
344 | try {
345 | ContentResolver cr = getContentResolver();
346 | InputStream in = cr.openInputStream(dataUri);
347 | Log.i(TAG, "incoming URI: " + dataUri.toString());
348 | String fileName = dataUri.getLastPathSegment();
349 | File f = new File("/" + fileName);
350 | BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(f));
351 | readBytesAndClose(in, out);
352 | Log.v(TAG, f.getAbsolutePath() + " size: " + String.valueOf(f.length()));
353 | } catch (Exception e) {
354 | Log.e(TAG, e.getMessage(), e);
355 | }
356 | }
357 |
358 | private void readBytesAndClose(InputStream in, OutputStream out)
359 | throws IOException {
360 | try {
361 | int block = 8 * 1024; // IOCipher works best with 8k blocks
362 | byte[] buff = new byte[block];
363 | while (true) {
364 | int len = in.read(buff, 0, block);
365 | if (len < 0) {
366 | break;
367 | }
368 | out.write(buff, 0, len);
369 | }
370 | } finally {
371 | in.close();
372 | out.flush();
373 | out.close();
374 | }
375 | }
376 |
377 | // To make listview for the list of file
378 | public void getFileList(String dirPath) {
379 |
380 | item = new ArrayList();
381 | path = new ArrayList();
382 |
383 | File file = new File(dirPath);
384 | File[] files = file.listFiles();
385 |
386 | if (!dirPath.equals(root)) {
387 | item.add(root);
388 | path.add(root);// to get back to main list
389 |
390 | item.add("..");
391 | path.add(file.getParent()); // back one level
392 | }
393 |
394 | for (int i = files.length-1; i >= 0; i--) {
395 |
396 | File fileItem = files[i];
397 | path.add(fileItem.getPath());
398 | if (fileItem.isDirectory()) {
399 | // input name directory to array list
400 | item.add("[" + fileItem.getName() + "]");
401 | } else {
402 | // input name file to array list
403 | item.add(fileItem.getName());
404 | }
405 | }
406 |
407 | // declare array with specific number of items
408 | items = new String[item.size()];
409 | // send data arraylist(item) to array(items)
410 | item.toArray(items);
411 | gridview.setAdapter(new IconicList());
412 |
413 | gridview.setOnItemClickListener(new OnItemClickListener() {
414 | public void onItemClick(AdapterView> parent, View v,
415 | int position, long id) {
416 |
417 | File file = new File(path.get(position));
418 |
419 | if (file.isDirectory()) {
420 | if (file.canRead()) {
421 | getFileList(path.get(position));
422 | } else {
423 | //show error
424 |
425 | }
426 | } else {
427 | showItem (file);
428 | }
429 | }
430 |
431 | });
432 |
433 | gridview.setOnItemLongClickListener(new OnItemLongClickListener () {
434 |
435 | @Override
436 | public boolean onItemLongClick(AdapterView> arg0, View arg1,
437 | int position, long arg3) {
438 |
439 | File file = new File(path.get(position));
440 | if (file.isDirectory()) {
441 | if (file.canRead()) {
442 | getFileList(path.get(position));
443 | return true;
444 | } else {
445 | //show error
446 |
447 | }
448 | } else {
449 | showItemDialog (file);
450 | return true;
451 | }
452 |
453 | return false;
454 | }
455 |
456 | });
457 |
458 | }
459 |
460 | private void showItemDialog (final File file)
461 | {
462 |
463 | new AlertDialog.Builder(GalleryActivity.this)
464 | .setIcon(R.drawable.ic_launcher)
465 | .setTitle("[" + file.getName() + "]")
466 | .setNegativeButton("Delete",
467 | new DialogInterface.OnClickListener() {
468 |
469 | public void onClick(DialogInterface dialog,
470 | int which) {
471 |
472 | file.delete();
473 | getFileList(root);
474 | }
475 |
476 | })
477 | .setPositiveButton("Share...",
478 | new DialogInterface.OnClickListener() {
479 |
480 | // @Override
481 | public void onClick(DialogInterface dialog,
482 | int which) {
483 |
484 | //Log.i(TAG,"open URL: " + Uri.parse(IOCipherContentProvider.FILES_URI + file.getName()));
485 | String sharePath = IOCipherContentProvider.addShare(file.getName(), IOCipherContentProvider.DEFAULT_AUTHORITY);
486 | Uri uri = Uri.parse(sharePath);
487 |
488 |
489 | //java.io.File exportFile = exportToDisk(file);
490 | //Uri uriExport = Uri.fromFile(exportFile);
491 |
492 | Intent intent = new Intent(Intent.ACTION_SEND);
493 |
494 | String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
495 | String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
496 | if (fileExtension.equals("mp4")||fileExtension.equals("mkv")||fileExtension.equals("mov"))
497 | mimeType = "video/*";
498 | if (mimeType == null)
499 | mimeType = "application/octet-stream";
500 |
501 | intent.setDataAndType(uri, mimeType);
502 | intent.putExtra(Intent.EXTRA_STREAM, uri);
503 | intent.putExtra(Intent.EXTRA_SUBJECT, file.getName());
504 | intent.putExtra(Intent.EXTRA_TITLE, file.getName());
505 |
506 | try {
507 | startActivity(Intent.createChooser(intent, "Share this!"));
508 | } catch (ActivityNotFoundException e) {
509 | Log.e(TAG, "No relevant Activity found", e);
510 | }
511 | }
512 | }).show();
513 | }
514 |
515 | private void showItem (File file)
516 | {
517 | try {
518 | String fileExtension = MimeTypeMap.getFileExtensionFromUrl(file.getAbsolutePath());
519 | String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
520 | if (fileExtension.equals("ts"))
521 | mimeType = "application/mpeg*";
522 |
523 | if (mimeType == null)
524 | mimeType = "application/octet-stream";
525 |
526 | if (mimeType.startsWith("image"))
527 | {
528 | Intent intent = new Intent(GalleryActivity.this,ImageViewerActivity.class);
529 | intent.setType(mimeType);
530 | intent.putExtra("vfs", file.getAbsolutePath());
531 | startActivity(intent);
532 | }
533 | else if (fileExtension.equals("mp4") || mimeType.startsWith("video"))
534 | {
535 | Intent intent = new Intent(GalleryActivity.this,MjpegViewerActivity.class);
536 | intent.setType(mimeType);
537 | intent.putExtra("video", file.getAbsolutePath());
538 |
539 | startActivity(intent);
540 |
541 | }
542 | else {
543 |
544 | String sharePath = IOCipherContentProvider.addShare(file.getName(), IOCipherContentProvider.DEFAULT_AUTHORITY);
545 | Uri uri = Uri.parse(sharePath);
546 |
547 | Intent intent = new Intent(Intent.ACTION_VIEW);
548 | intent.setDataAndType(uri, mimeType);
549 | startActivity(intent);
550 | }
551 |
552 |
553 | } catch (ActivityNotFoundException e) {
554 | Log.e(TAG, "No relevant Activity found", e);
555 | }
556 | }
557 |
558 | static class ViewHolder {
559 |
560 | ImageView icon;
561 |
562 | }
563 |
564 | class IconicList extends ArrayAdapter