├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ ├── Rex___AirPair.xml │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── libraries │ ├── appcompat_v7_19_0_1.xml │ └── support_v4_19_0_1.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── README.md ├── android-camera.png ├── build.gradle ├── camera ├── .gitignore ├── build.gradle ├── camera.iml ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── ultimate │ │ └── camera │ │ ├── NavigationDrawerFragment.java │ │ ├── activities │ │ ├── CameraActivity.java │ │ └── MainActivity.java │ │ ├── adapters │ │ ├── PhotoAdapter.java │ │ └── items │ │ │ └── PhotoItem.java │ │ ├── fragments │ │ ├── BaseFragment.java │ │ ├── HorizontalPhotoGalleryFragment.java │ │ ├── NativeCameraFragment.java │ │ ├── SimpleAndroidImagePickerFragment.java │ │ ├── SimpleCameraIntentFragment.java │ │ └── SimplePhotoGalleryListFragment.java │ │ ├── utilities │ │ ├── DeviceInfo.java │ │ ├── DialogHelper.java │ │ ├── ImageUtil.java │ │ ├── PhotoGalleryAsyncLoader.java │ │ └── PhotoGalleryImageProvider.java │ │ └── views │ │ └── TwoWayView.java │ └── res │ ├── drawable-hdpi │ ├── drawer_shadow.9.png │ ├── ic_drawer.png │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── drawer_shadow.9.png │ ├── ic_drawer.png │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── drawer_shadow.9.png │ ├── ic_drawer.png │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── drawer_shadow.9.png │ ├── ic_drawer.png │ └── ic_launcher.png │ ├── drawable │ └── placeholder.png │ ├── layout │ ├── activity_main.xml │ ├── fragment_horizontal_gallery.xml │ ├── fragment_main.xml │ ├── fragment_native_camera.xml │ ├── fragment_navigation_drawer.xml │ ├── fragment_photo_gallery.xml │ ├── fragment_photo_picker.xml │ ├── fragment_simple_camera_intent.xml │ └── photo_item.xml │ ├── menu │ ├── global.xml │ └── main.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── ultimate.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | ultimate -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/copyright/Rex___AirPair.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_19_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_19_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Android Camera Guide

2 | 3 |

ULTIMATE ANDROID CAMERA GUIDE

4 | Presented by www.AirPair.com 5 | by Rex St. John 6 | www.rexstjohn.com 7 | twitter: @rexstjohn 8 | 9 | The official "Ultimate Android Camera Guide" source repo. 10 | 11 | Includes examples on how to.. 12 | 13 | 20 | 21 | INSTRUCTIONS 22 | 23 | Download / clone. Open in Android Studio. Run. The end. 24 | 25 | ATTRIBUTION 26 | 27 | This project makes use of source code snippets provided by Google's Android development portal and TwoWayView by Lucas Rocha: https://github.com/lucasr/twoway-view. Certain code portions are derived partially from results found on StackOverflow during research: 28 | 29 | 32 | 33 | LICENSE 34 | 35 | Copyright (c) 2014 Rex St John, @rexstjohn 36 | 37 | Permission is hereby granted, free of charge, to any person obtaining a copy 38 | of this software and associated documentation files (the "Software"), to deal 39 | in the Software without restriction, including without limitation the rights 40 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 | copies of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be included in 45 | all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 53 | THE SOFTWARE. 54 | -------------------------------------------------------------------------------- /android-camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/android-camera.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:0.9.+' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | mavenCentral() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /camera/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /camera/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android' 2 | 3 | android { 4 | compileSdkVersion 19 5 | buildToolsVersion "19.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 11 9 | targetSdkVersion 19 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | debug { 15 | } 16 | release { 17 | runProguard false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 19 | } 20 | } 21 | signingConfigs { 22 | debug { 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile 'com.android.support:appcompat-v7:+' 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | } 31 | -------------------------------------------------------------------------------- /camera/camera.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /camera/proguard-rules.txt: -------------------------------------------------------------------------------- 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 ProGuard 5 | # include property in project.properties. 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 | #} -------------------------------------------------------------------------------- /camera/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /camera/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/NavigationDrawerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera; 24 | 25 | import android.support.v7.app.ActionBarActivity; 26 | import android.app.Activity; 27 | import android.support.v7.app.ActionBar; 28 | import android.support.v4.app.Fragment; 29 | import android.support.v4.app.ActionBarDrawerToggle; 30 | import android.support.v4.view.GravityCompat; 31 | import android.support.v4.widget.DrawerLayout; 32 | import android.content.SharedPreferences; 33 | import android.content.res.Configuration; 34 | import android.os.Bundle; 35 | import android.preference.PreferenceManager; 36 | import android.view.LayoutInflater; 37 | import android.view.Menu; 38 | import android.view.MenuInflater; 39 | import android.view.MenuItem; 40 | import android.view.View; 41 | import android.view.ViewGroup; 42 | import android.widget.AdapterView; 43 | import android.widget.ArrayAdapter; 44 | import android.widget.ListView; 45 | import android.widget.Toast; 46 | 47 | /** 48 | * Fragment used for managing interactions for and presentation of a navigation drawer. 49 | * See the 50 | * design guidelines for a complete explanation of the behaviors implemented here. 51 | */ 52 | public class NavigationDrawerFragment extends Fragment { 53 | 54 | /** 55 | * Remember the position of the selected item. 56 | */ 57 | private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; 58 | 59 | /** 60 | * Per the design guidelines, you should show the drawer on launch until the user manually 61 | * expands it. This shared preference tracks this. 62 | */ 63 | private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; 64 | 65 | /** 66 | * A pointer to the current callbacks instance (the Activity). 67 | */ 68 | private NavigationDrawerCallbacks mCallbacks; 69 | 70 | /** 71 | * Helper component that ties the action bar to the navigation drawer. 72 | */ 73 | private ActionBarDrawerToggle mDrawerToggle; 74 | 75 | private DrawerLayout mDrawerLayout; 76 | private ListView mDrawerListView; 77 | private View mFragmentContainerView; 78 | 79 | private int mCurrentSelectedPosition = 0; 80 | private boolean mFromSavedInstanceState; 81 | private boolean mUserLearnedDrawer; 82 | 83 | public NavigationDrawerFragment() { 84 | } 85 | 86 | @Override 87 | public void onCreate(Bundle savedInstanceState) { 88 | super.onCreate(savedInstanceState); 89 | 90 | // Read in the flag indicating whether or not the user has demonstrated awareness of the 91 | // drawer. See PREF_USER_LEARNED_DRAWER for details. 92 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); 93 | mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); 94 | 95 | if (savedInstanceState != null) { 96 | mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); 97 | mFromSavedInstanceState = true; 98 | } 99 | 100 | // Select either the default item (0) or the last selected item. 101 | selectItem(mCurrentSelectedPosition); 102 | } 103 | 104 | @Override 105 | public void onActivityCreated (Bundle savedInstanceState) { 106 | super.onActivityCreated(savedInstanceState); 107 | // Indicate that this fragment would like to influence the set of actions in the action bar. 108 | setHasOptionsMenu(true); 109 | } 110 | 111 | @Override 112 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 113 | Bundle savedInstanceState) { 114 | mDrawerListView = (ListView) inflater.inflate( 115 | R.layout.fragment_navigation_drawer, container, false); 116 | mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 117 | @Override 118 | public void onItemClick(AdapterView parent, View view, int position, long id) { 119 | selectItem(position); 120 | } 121 | }); 122 | mDrawerListView.setAdapter(new ArrayAdapter( 123 | getActionBar().getThemedContext(), 124 | android.R.layout.simple_list_item_activated_1, 125 | android.R.id.text1, 126 | new String[]{ 127 | getString(R.string.title_section1), 128 | getString(R.string.title_section2), 129 | getString(R.string.title_section3), 130 | getString(R.string.title_section4), 131 | getString(R.string.title_section5) 132 | })); 133 | mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); 134 | return mDrawerListView; 135 | } 136 | 137 | public boolean isDrawerOpen() { 138 | return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); 139 | } 140 | 141 | /** 142 | * Users of this fragment must call this method to set up the navigation drawer interactions. 143 | * 144 | * @param fragmentId The android:id of this fragment in its activity's layout. 145 | * @param drawerLayout The DrawerLayout containing this fragment's UI. 146 | */ 147 | public void setUp(int fragmentId, DrawerLayout drawerLayout) { 148 | mFragmentContainerView = getActivity().findViewById(fragmentId); 149 | mDrawerLayout = drawerLayout; 150 | 151 | // set a custom shadow that overlays the main content when the drawer opens 152 | mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); 153 | // set up the drawer's list view with items and click listener 154 | 155 | ActionBar actionBar = getActionBar(); 156 | actionBar.setDisplayHomeAsUpEnabled(true); 157 | actionBar.setHomeButtonEnabled(true); 158 | 159 | // ActionBarDrawerToggle ties together the the proper interactions 160 | // between the navigation drawer and the action bar app icon. 161 | mDrawerToggle = new ActionBarDrawerToggle( 162 | getActivity(), /* host Activity */ 163 | mDrawerLayout, /* DrawerLayout object */ 164 | R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ 165 | R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ 166 | R.string.navigation_drawer_close /* "close drawer" description for accessibility */ 167 | ) { 168 | @Override 169 | public void onDrawerClosed(View drawerView) { 170 | super.onDrawerClosed(drawerView); 171 | if (!isAdded()) { 172 | return; 173 | } 174 | 175 | getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() 176 | } 177 | 178 | @Override 179 | public void onDrawerOpened(View drawerView) { 180 | super.onDrawerOpened(drawerView); 181 | if (!isAdded()) { 182 | return; 183 | } 184 | 185 | if (!mUserLearnedDrawer) { 186 | // The user manually opened the drawer; store this flag to prevent auto-showing 187 | // the navigation drawer automatically in the future. 188 | mUserLearnedDrawer = true; 189 | SharedPreferences sp = PreferenceManager 190 | .getDefaultSharedPreferences(getActivity()); 191 | sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); 192 | } 193 | 194 | getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() 195 | } 196 | }; 197 | 198 | // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, 199 | // per the navigation drawer design guidelines. 200 | if (!mUserLearnedDrawer && !mFromSavedInstanceState) { 201 | mDrawerLayout.openDrawer(mFragmentContainerView); 202 | } 203 | 204 | // Defer code dependent on restoration of previous instance state. 205 | mDrawerLayout.post(new Runnable() { 206 | @Override 207 | public void run() { 208 | mDrawerToggle.syncState(); 209 | } 210 | }); 211 | 212 | mDrawerLayout.setDrawerListener(mDrawerToggle); 213 | } 214 | 215 | private void selectItem(int position) { 216 | mCurrentSelectedPosition = position; 217 | if (mDrawerListView != null) { 218 | mDrawerListView.setItemChecked(position, true); 219 | } 220 | if (mDrawerLayout != null) { 221 | mDrawerLayout.closeDrawer(mFragmentContainerView); 222 | } 223 | if (mCallbacks != null) { 224 | mCallbacks.onNavigationDrawerItemSelected(position); 225 | } 226 | } 227 | 228 | @Override 229 | public void onAttach(Activity activity) { 230 | super.onAttach(activity); 231 | try { 232 | mCallbacks = (NavigationDrawerCallbacks) activity; 233 | } catch (ClassCastException e) { 234 | throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); 235 | } 236 | } 237 | 238 | @Override 239 | public void onDetach() { 240 | super.onDetach(); 241 | mCallbacks = null; 242 | } 243 | 244 | @Override 245 | public void onSaveInstanceState(Bundle outState) { 246 | super.onSaveInstanceState(outState); 247 | outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); 248 | } 249 | 250 | @Override 251 | public void onConfigurationChanged(Configuration newConfig) { 252 | super.onConfigurationChanged(newConfig); 253 | // Forward the new configuration the drawer toggle component. 254 | mDrawerToggle.onConfigurationChanged(newConfig); 255 | } 256 | 257 | @Override 258 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 259 | // If the drawer is open, show the global app actions in the action bar. See also 260 | // showGlobalContextActionBar, which controls the top-left area of the action bar. 261 | if (mDrawerLayout != null && isDrawerOpen()) { 262 | inflater.inflate(R.menu.global, menu); 263 | showGlobalContextActionBar(); 264 | } 265 | super.onCreateOptionsMenu(menu, inflater); 266 | } 267 | 268 | @Override 269 | public boolean onOptionsItemSelected(MenuItem item) { 270 | if (mDrawerToggle.onOptionsItemSelected(item)) { 271 | return true; 272 | } 273 | 274 | return super.onOptionsItemSelected(item); 275 | } 276 | 277 | /** 278 | * Per the navigation drawer design guidelines, updates the action bar to show the global app 279 | * 'context', rather than just what's in the current screen. 280 | */ 281 | private void showGlobalContextActionBar() { 282 | ActionBar actionBar = getActionBar(); 283 | actionBar.setDisplayShowTitleEnabled(true); 284 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 285 | actionBar.setTitle(R.string.app_name); 286 | } 287 | 288 | private ActionBar getActionBar() { 289 | return ((ActionBarActivity) getActivity()).getSupportActionBar(); 290 | } 291 | 292 | /** 293 | * Callbacks interface that all activities using this fragment must implement. 294 | */ 295 | public static interface NavigationDrawerCallbacks { 296 | /** 297 | * Called when an item in the navigation drawer is selected. 298 | */ 299 | void onNavigationDrawerItemSelected(int position); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/activities/CameraActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.activities; 24 | 25 | import android.net.Uri; 26 | import android.os.Bundle; 27 | import android.support.v7.app.ActionBarActivity; 28 | 29 | /** 30 | * This activity assists in trapping the camera's "State" e.g. where the camera plans 31 | * on saving it's resulting file and URI. This activity saves this information to the bundle 32 | * and retrieves it on resume. This is necessary because when the Android external camera starts, 33 | * the file path anf URI get collected and won't be available on resume, resulting in a crash. 34 | *

35 | * Samsung devices, in particular may crash if you don't do this: 36 | * Reference: http://stackoverflow.com/questions/8248327/my-android-camera-uri-is-returning-a-null-value-but-the-samsung-fix-is-in-place 37 | *

38 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 39 | */ 40 | public class CameraActivity extends ActionBarActivity { 41 | 42 | // Storage for camera image URI components 43 | private final static String CAPTURED_PHOTO_PATH_KEY = "mCurrentPhotoPath"; 44 | private final static String CAPTURED_PHOTO_URI_KEY = "mCapturedImageURI"; 45 | 46 | // Required for camera operations in order to save the image file on resume. 47 | private String mCurrentPhotoPath = null; 48 | private Uri mCapturedImageURI = null; 49 | 50 | @Override 51 | public void onSaveInstanceState(Bundle savedInstanceState) { 52 | if (mCurrentPhotoPath != null) { 53 | savedInstanceState.putString(CAPTURED_PHOTO_PATH_KEY, mCurrentPhotoPath); 54 | } 55 | if (mCapturedImageURI != null) { 56 | savedInstanceState.putString(CAPTURED_PHOTO_URI_KEY, mCapturedImageURI.toString()); 57 | } 58 | super.onSaveInstanceState(savedInstanceState); 59 | } 60 | 61 | @Override 62 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 63 | if (savedInstanceState.containsKey(CAPTURED_PHOTO_PATH_KEY)) { 64 | mCurrentPhotoPath = savedInstanceState.getString(CAPTURED_PHOTO_PATH_KEY); 65 | } 66 | if (savedInstanceState.containsKey(CAPTURED_PHOTO_URI_KEY)) { 67 | mCapturedImageURI = Uri.parse(savedInstanceState.getString(CAPTURED_PHOTO_URI_KEY)); 68 | } 69 | super.onRestoreInstanceState(savedInstanceState); 70 | } 71 | 72 | /** 73 | * Getters and setters. 74 | */ 75 | 76 | public String getCurrentPhotoPath() { 77 | return mCurrentPhotoPath; 78 | } 79 | 80 | public void setCurrentPhotoPath(String mCurrentPhotoPath) { 81 | this.mCurrentPhotoPath = mCurrentPhotoPath; 82 | } 83 | 84 | public Uri getCapturedImageURI() { 85 | return mCapturedImageURI; 86 | } 87 | 88 | public void setCapturedImageURI(Uri mCapturedImageURI) { 89 | this.mCapturedImageURI = mCapturedImageURI; 90 | } 91 | } -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.activities; 24 | 25 | import android.app.FragmentManager; 26 | import android.net.Uri; 27 | import android.os.Bundle; 28 | import android.support.v4.widget.DrawerLayout; 29 | import android.support.v7.app.ActionBar; 30 | import android.view.Menu; 31 | import android.view.MenuItem; 32 | 33 | import com.ultimate.camera.NavigationDrawerFragment; 34 | import com.ultimate.camera.R; 35 | import com.ultimate.camera.fragments.BaseFragment; 36 | import com.ultimate.camera.fragments.HorizontalPhotoGalleryFragment; 37 | import com.ultimate.camera.fragments.NativeCameraFragment; 38 | import com.ultimate.camera.fragments.SimpleAndroidImagePickerFragment; 39 | import com.ultimate.camera.fragments.SimpleCameraIntentFragment; 40 | import com.ultimate.camera.fragments.SimplePhotoGalleryListFragment; 41 | 42 | /** 43 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 44 | */ 45 | public class MainActivity extends CameraActivity 46 | implements NavigationDrawerFragment.NavigationDrawerCallbacks, BaseFragment.OnFragmentInteractionListener { 47 | 48 | /** 49 | * Actions 50 | */ 51 | public static final int SELECT_PHOTO_ACTION = 0; 52 | 53 | /** 54 | * Fragment Identifiers 55 | */ 56 | public static final int SIMPLE_CAMERA_INTENT_FRAGMENT = 0; 57 | public static final int SIMPLE_PHOTO_GALLERY_FRAGMENT = 1; 58 | public static final int SIMPLE_PHOTO_PICKER_FRAGMENT = 2; 59 | public static final int NATIVE_CAMERA_FRAGMENT = 3; 60 | public static final int HORIZONTAL_GALLERY_FRAGMENT = 4; 61 | 62 | /** 63 | * Fragment managing the behaviors, interactions and presentation of the navigation drawer. 64 | */ 65 | private NavigationDrawerFragment mNavigationDrawerFragment; 66 | 67 | /** 68 | * Used to store the last screen title. For use in {@link #restoreActionBar()}. 69 | */ 70 | private CharSequence mTitle; 71 | 72 | @Override 73 | protected void onCreate(Bundle savedInstanceState) { 74 | super.onCreate(savedInstanceState); 75 | setContentView(R.layout.activity_main); 76 | 77 | mNavigationDrawerFragment = (NavigationDrawerFragment) 78 | getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); 79 | mTitle = getTitle(); 80 | 81 | // Set up the drawer. 82 | mNavigationDrawerFragment.setUp( 83 | R.id.navigation_drawer, 84 | (DrawerLayout) findViewById(R.id.drawer_layout)); 85 | } 86 | 87 | @Override 88 | public void onNavigationDrawerItemSelected(int position) { 89 | 90 | // update the main content by replacing fragments 91 | FragmentManager fragmentManager = getFragmentManager(); 92 | BaseFragment targetFragment = null; 93 | 94 | // Populate the fragment 95 | switch (position) { 96 | case SIMPLE_CAMERA_INTENT_FRAGMENT: { 97 | targetFragment = SimpleCameraIntentFragment.newInstance(position + 1); 98 | break; 99 | } 100 | case SIMPLE_PHOTO_GALLERY_FRAGMENT: { 101 | targetFragment = SimplePhotoGalleryListFragment.newInstance(position + 1); 102 | break; 103 | } 104 | case SIMPLE_PHOTO_PICKER_FRAGMENT: { 105 | targetFragment = SimpleAndroidImagePickerFragment.newInstance(position + 1); 106 | break; 107 | } 108 | case NATIVE_CAMERA_FRAGMENT: { 109 | targetFragment = NativeCameraFragment.newInstance(position + 1); 110 | break; 111 | } 112 | case HORIZONTAL_GALLERY_FRAGMENT:{ 113 | targetFragment = HorizontalPhotoGalleryFragment.newInstance(position + 1); 114 | break; 115 | } 116 | default: 117 | break; 118 | } 119 | 120 | // Select the fragment. 121 | fragmentManager.beginTransaction() 122 | .replace(R.id.container, targetFragment) 123 | .commit(); 124 | } 125 | 126 | public void onSectionAttached(int number) { 127 | switch (number) { 128 | case 1: 129 | mTitle = getString(R.string.title_section1); 130 | break; 131 | case 2: 132 | mTitle = getString(R.string.title_section2); 133 | break; 134 | case 3: 135 | mTitle = getString(R.string.title_section3); 136 | break; 137 | case 4: 138 | mTitle = getString(R.string.title_section4); 139 | break; 140 | case 5: 141 | mTitle = getString(R.string.title_section5); 142 | break; 143 | } 144 | } 145 | 146 | public void restoreActionBar() { 147 | ActionBar actionBar = getSupportActionBar(); 148 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 149 | actionBar.setDisplayShowTitleEnabled(true); 150 | actionBar.setTitle(mTitle); 151 | } 152 | 153 | @Override 154 | public boolean onCreateOptionsMenu(Menu menu) { 155 | if (!mNavigationDrawerFragment.isDrawerOpen()) { 156 | // Only show items in the action bar relevant to this screen 157 | // if the drawer is not showing. Otherwise, let the drawer 158 | // decide what to show in the action bar. 159 | getMenuInflater().inflate(R.menu.main, menu); 160 | restoreActionBar(); 161 | return true; 162 | } 163 | return super.onCreateOptionsMenu(menu); 164 | } 165 | 166 | @Override 167 | public boolean onOptionsItemSelected(MenuItem item) { 168 | // Handle action bar item clicks here. The action bar will 169 | // automatically handle clicks on the Home/Up button, so long 170 | // as you specify a parent activity in AndroidManifest.xml. 171 | int id = item.getItemId(); 172 | return super.onOptionsItemSelected(item); 173 | } 174 | 175 | /** 176 | * Handle Incoming messages from contained fragments. 177 | */ 178 | 179 | @Override 180 | public void onFragmentInteraction(Uri uri) { 181 | 182 | } 183 | 184 | @Override 185 | public void onFragmentInteraction(String id) { 186 | 187 | } 188 | 189 | @Override 190 | public void onFragmentInteraction(int actionId) { 191 | 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/adapters/PhotoAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.adapters; 24 | 25 | import android.app.Activity; 26 | import android.content.Context; 27 | import android.view.LayoutInflater; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | import android.widget.ArrayAdapter; 31 | import android.widget.CheckBox; 32 | import android.widget.CompoundButton; 33 | import android.widget.ImageView; 34 | 35 | import com.ultimate.camera.R; 36 | import com.ultimate.camera.adapters.items.PhotoItem; 37 | 38 | import java.util.List; 39 | 40 | /** 41 | * Photo Array Adapter to power a simple Android photo gallery. 42 | * 43 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 44 | */ 45 | public class PhotoAdapter extends ArrayAdapter{ 46 | 47 | // Ivars. 48 | private Context context; 49 | private int resourceId; 50 | 51 | public PhotoAdapter(Context context, int resourceId, 52 | List items, boolean useList) { 53 | super(context, resourceId, items); 54 | this.context = context; 55 | this.resourceId = resourceId; 56 | } 57 | 58 | /** 59 | * The "ViewHolder" pattern is used for speed. 60 | * 61 | * Reference: http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html 62 | */ 63 | private class ViewHolder { 64 | ImageView photoImageView; 65 | } 66 | 67 | /** 68 | * Populate the view holder with data. 69 | * @param position 70 | * @param convertView 71 | * @param parent 72 | * @return 73 | */ 74 | public View getView(int position, View convertView, ViewGroup parent) { 75 | ViewHolder holder = null; 76 | PhotoItem photoItem = getItem(position); 77 | View viewToUse = null; 78 | 79 | // This block exists to inflate the photo list item conditionally based on whether 80 | // we want to support a grid or list view. 81 | LayoutInflater mInflater = (LayoutInflater) context 82 | .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); 83 | if (convertView == null) { 84 | holder = new ViewHolder(); 85 | viewToUse = mInflater.inflate(resourceId, null); 86 | holder.photoImageView = (ImageView) viewToUse.findViewById(R.id.imageView); 87 | viewToUse.setTag(holder); 88 | } else { 89 | viewToUse = convertView; 90 | holder = (ViewHolder) viewToUse.getTag(); 91 | } 92 | 93 | // Set the thumbnail 94 | holder.photoImageView.setImageURI(photoItem.getThumbnailUri()); 95 | 96 | return viewToUse; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/adapters/items/PhotoItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.adapters.items; 24 | 25 | import android.net.Uri; 26 | 27 | /** 28 | * Used to represent a photo item. 29 | * 30 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 31 | */ 32 | public class PhotoItem { 33 | 34 | // Ivars. 35 | private Uri thumbnailUri; 36 | private Uri fullImageUri; 37 | 38 | public PhotoItem(Uri thumbnailUri,Uri fullImageUri) { 39 | this.thumbnailUri = thumbnailUri; 40 | this.fullImageUri = fullImageUri; 41 | } 42 | 43 | /** 44 | * Getters and setters 45 | */ 46 | public Uri getThumbnailUri() { 47 | return thumbnailUri; 48 | } 49 | 50 | public void setThumbnailUri(Uri thumbnailUri) { 51 | this.thumbnailUri = thumbnailUri; 52 | } 53 | 54 | public Uri getFullImageUri() { 55 | return fullImageUri; 56 | } 57 | 58 | public void setFullImageUri(Uri fullImageUri) { 59 | this.fullImageUri = fullImageUri; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.fragments; 24 | 25 | import android.app.Fragment; 26 | import android.net.Uri; 27 | import android.os.Bundle; 28 | 29 | /** 30 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 31 | */ 32 | public class BaseFragment extends Fragment { 33 | 34 | public static final String ARG_SECTION_NUMBER = "ARG_SECTION_NUMBER"; 35 | 36 | /** 37 | * Default empty constructor 38 | */ 39 | public BaseFragment(){ 40 | // 41 | } 42 | 43 | /** 44 | * This interface must be implemented by activities that contain this 45 | * mFragment to allow an interaction in this mFragment to be communicated 46 | * to the mActivity and potentially other fragments contained in that 47 | * mActivity. 48 | *

49 | * See the Android Training lesson Communicating with Other Fragments for more information. 52 | */ 53 | public interface OnFragmentInteractionListener { 54 | public void onFragmentInteraction(Uri uri); 55 | public void onFragmentInteraction(String id); 56 | public void onFragmentInteraction(int actionId); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/fragments/HorizontalPhotoGalleryFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.fragments; 24 | 25 | import android.os.Bundle; 26 | import android.view.LayoutInflater; 27 | import android.view.View; 28 | import android.view.ViewGroup; 29 | import android.widget.AbsListView; 30 | import android.widget.AdapterView; 31 | import android.widget.ListAdapter; 32 | import android.widget.TextView; 33 | 34 | import com.ultimate.camera.R; 35 | import com.ultimate.camera.views.TwoWayView; 36 | 37 | /** 38 | * A demonstration of showing images in a horizontal image gallery. 39 | * 40 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 41 | */ 42 | public class HorizontalPhotoGalleryFragment extends SimplePhotoGalleryListFragment { 43 | 44 | // Left and right scrolling two way view 45 | private TwoWayView mHorizontalListView; 46 | 47 | public HorizontalPhotoGalleryFragment(){ 48 | super(); 49 | } 50 | 51 | /** 52 | * Static factory method 53 | * @param sectionNumber 54 | * @return 55 | */ 56 | public static HorizontalPhotoGalleryFragment newInstance(int sectionNumber) { 57 | HorizontalPhotoGalleryFragment fragment = new HorizontalPhotoGalleryFragment(); 58 | Bundle args = new Bundle(); 59 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 60 | fragment.setArguments(args); 61 | return fragment; 62 | } 63 | 64 | /** 65 | * Create View! 66 | * @param inflater 67 | * @param container 68 | * @param savedInstanceState 69 | * @return 70 | */ 71 | @Override 72 | public View onCreateView(LayoutInflater inflater, 73 | ViewGroup container, 74 | Bundle savedInstanceState) { 75 | View view = null; 76 | view = inflater.inflate(R.layout.fragment_horizontal_gallery, container, false); 77 | 78 | // Set the mAdapter 79 | mEmptyTextView = (TextView)view.findViewById(R.id.empty); 80 | mHorizontalListView = (TwoWayView) view.findViewById(R.id.horizontalList); 81 | mHorizontalListView.setAdapter(mAdapter); 82 | mHorizontalListView.setItemMargin(10); 83 | 84 | resolveEmptyText(); 85 | 86 | return view; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/fragments/NativeCameraFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.fragments; 24 | 25 | import android.content.Context; 26 | import android.hardware.Camera; 27 | import android.os.Bundle; 28 | import android.os.Environment; 29 | import android.util.Log; 30 | import android.view.Display; 31 | import android.view.LayoutInflater; 32 | import android.view.Surface; 33 | import android.view.SurfaceHolder; 34 | import android.view.SurfaceView; 35 | import android.view.View; 36 | import android.view.ViewGroup; 37 | import android.view.WindowManager; 38 | import android.widget.Button; 39 | import android.widget.FrameLayout; 40 | import android.widget.ImageView; 41 | import android.widget.Toast; 42 | 43 | import com.ultimate.camera.R; 44 | import com.ultimate.camera.utilities.DialogHelper; 45 | 46 | import java.io.File; 47 | import java.io.FileNotFoundException; 48 | import java.io.FileOutputStream; 49 | import java.io.IOException; 50 | import java.text.SimpleDateFormat; 51 | import java.util.Date; 52 | import java.util.List; 53 | 54 | /** 55 | * Take a picture directly from inside the app using this fragment. 56 | * 57 | * Reference: http://developer.android.com/training/camera/cameradirect.html 58 | * Reference: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails 59 | * Reference: http://stackoverflow.com/questions/10913181/camera-preview-is-not-restarting 60 | * 61 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 62 | */ 63 | public class NativeCameraFragment extends BaseFragment { 64 | 65 | // Native camera. 66 | private Camera mCamera; 67 | 68 | // View to display the camera output. 69 | private CameraPreview mPreview; 70 | 71 | // Reference to the containing view. 72 | private View mCameraView; 73 | 74 | /** 75 | * Default empty constructor. 76 | */ 77 | public NativeCameraFragment(){ 78 | super(); 79 | } 80 | 81 | /** 82 | * Static factory method 83 | * @param sectionNumber 84 | * @return 85 | */ 86 | public static NativeCameraFragment newInstance(int sectionNumber) { 87 | NativeCameraFragment fragment = new NativeCameraFragment(); 88 | Bundle args = new Bundle(); 89 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 90 | fragment.setArguments(args); 91 | return fragment; 92 | } 93 | 94 | /** 95 | * OnCreateView fragment override 96 | * @param inflater 97 | * @param container 98 | * @param savedInstanceState 99 | * @return 100 | */ 101 | @Override 102 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 103 | Bundle savedInstanceState) { 104 | View view = inflater.inflate(R.layout.fragment_native_camera, container, false); 105 | 106 | // Create our Preview view and set it as the content of our activity. 107 | boolean opened = safeCameraOpenInView(view); 108 | 109 | if(opened == false){ 110 | Log.d("CameraGuide","Error, Camera failed to open"); 111 | return view; 112 | } 113 | 114 | // Trap the capture button. 115 | Button captureButton = (Button) view.findViewById(R.id.button_capture); 116 | captureButton.setOnClickListener( 117 | new View.OnClickListener() { 118 | @Override 119 | public void onClick(View v) { 120 | // get an image from the camera 121 | mCamera.takePicture(null, null, mPicture); 122 | } 123 | } 124 | ); 125 | 126 | return view; 127 | } 128 | 129 | /** 130 | * Recommended "safe" way to open the camera. 131 | * @param view 132 | * @return 133 | */ 134 | private boolean safeCameraOpenInView(View view) { 135 | boolean qOpened = false; 136 | releaseCameraAndPreview(); 137 | mCamera = getCameraInstance(); 138 | mCameraView = view; 139 | qOpened = (mCamera != null); 140 | 141 | if(qOpened == true){ 142 | mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera,view); 143 | FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview); 144 | preview.addView(mPreview); 145 | mPreview.startCameraPreview(); 146 | } 147 | return qOpened; 148 | } 149 | 150 | /** 151 | * Safe method for getting a camera instance. 152 | * @return 153 | */ 154 | public static Camera getCameraInstance(){ 155 | Camera c = null; 156 | try { 157 | c = Camera.open(); // attempt to get a Camera instance 158 | } 159 | catch (Exception e){ 160 | e.printStackTrace(); 161 | } 162 | return c; // returns null if camera is unavailable 163 | } 164 | 165 | @Override 166 | public void onPause() { 167 | super.onPause(); 168 | } 169 | 170 | @Override 171 | public void onDestroy() { 172 | super.onDestroy(); 173 | releaseCameraAndPreview(); 174 | } 175 | 176 | /** 177 | * Clear any existing preview / camera. 178 | */ 179 | private void releaseCameraAndPreview() { 180 | 181 | if (mCamera != null) { 182 | mCamera.stopPreview(); 183 | mCamera.release(); 184 | mCamera = null; 185 | } 186 | if(mPreview != null){ 187 | mPreview.destroyDrawingCache(); 188 | mPreview.mCamera = null; 189 | } 190 | } 191 | 192 | /** 193 | * Surface on which the camera projects it's capture results. This is derived both from Google's docs and the 194 | * excellent StackOverflow answer provided below. 195 | * 196 | * Reference / Credit: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails 197 | */ 198 | class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { 199 | 200 | // SurfaceHolder 201 | private SurfaceHolder mHolder; 202 | 203 | // Our Camera. 204 | private Camera mCamera; 205 | 206 | // Parent Context. 207 | private Context mContext; 208 | 209 | // Camera Sizing (For rotation, orientation changes) 210 | private Camera.Size mPreviewSize; 211 | 212 | // List of supported preview sizes 213 | private List mSupportedPreviewSizes; 214 | 215 | // Flash modes supported by this camera 216 | private List mSupportedFlashModes; 217 | 218 | // View holding this camera. 219 | private View mCameraView; 220 | 221 | public CameraPreview(Context context, Camera camera, View cameraView) { 222 | super(context); 223 | 224 | // Capture the context 225 | mCameraView = cameraView; 226 | mContext = context; 227 | setCamera(camera); 228 | 229 | // Install a SurfaceHolder.Callback so we get notified when the 230 | // underlying surface is created and destroyed. 231 | mHolder = getHolder(); 232 | mHolder.addCallback(this); 233 | mHolder.setKeepScreenOn(true); 234 | // deprecated setting, but required on Android versions prior to 3.0 235 | mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 236 | } 237 | 238 | /** 239 | * Begin the preview of the camera input. 240 | */ 241 | public void startCameraPreview() 242 | { 243 | try{ 244 | mCamera.setPreviewDisplay(mHolder); 245 | mCamera.startPreview(); 246 | } 247 | catch(Exception e){ 248 | e.printStackTrace(); 249 | } 250 | } 251 | 252 | /** 253 | * Extract supported preview and flash modes from the camera. 254 | * @param camera 255 | */ 256 | private void setCamera(Camera camera) 257 | { 258 | // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails 259 | mCamera = camera; 260 | mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); 261 | mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes(); 262 | 263 | // Set the camera to Auto Flash mode. 264 | if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)){ 265 | Camera.Parameters parameters = mCamera.getParameters(); 266 | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); 267 | mCamera.setParameters(parameters); 268 | } 269 | 270 | requestLayout(); 271 | } 272 | 273 | /** 274 | * The Surface has been created, now tell the camera where to draw the preview. 275 | * @param holder 276 | */ 277 | public void surfaceCreated(SurfaceHolder holder) { 278 | try { 279 | mCamera.setPreviewDisplay(holder); 280 | } catch (IOException e) { 281 | e.printStackTrace(); 282 | } 283 | } 284 | 285 | /** 286 | * Dispose of the camera preview. 287 | * @param holder 288 | */ 289 | public void surfaceDestroyed(SurfaceHolder holder) { 290 | if (mCamera != null){ 291 | mCamera.stopPreview(); 292 | } 293 | } 294 | 295 | /** 296 | * React to surface changed events 297 | * @param holder 298 | * @param format 299 | * @param w 300 | * @param h 301 | */ 302 | public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 303 | // If your preview can change or rotate, take care of those events here. 304 | // Make sure to stop the preview before resizing or reformatting it. 305 | 306 | if (mHolder.getSurface() == null){ 307 | // preview surface does not exist 308 | return; 309 | } 310 | 311 | // stop preview before making changes 312 | try { 313 | Camera.Parameters parameters = mCamera.getParameters(); 314 | 315 | // Set the auto-focus mode to "continuous" 316 | parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); 317 | 318 | // Preview size must exist. 319 | if(mPreviewSize != null) { 320 | Camera.Size previewSize = mPreviewSize; 321 | parameters.setPreviewSize(previewSize.width, previewSize.height); 322 | } 323 | 324 | mCamera.setParameters(parameters); 325 | mCamera.startPreview(); 326 | } catch (Exception e){ 327 | e.printStackTrace(); 328 | } 329 | } 330 | 331 | /** 332 | * Calculate the measurements of the layout 333 | * @param widthMeasureSpec 334 | * @param heightMeasureSpec 335 | */ 336 | @Override 337 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 338 | { 339 | // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails 340 | final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); 341 | final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); 342 | setMeasuredDimension(width, height); 343 | 344 | if (mSupportedPreviewSizes != null){ 345 | mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); 346 | } 347 | } 348 | 349 | /** 350 | * Update the layout based on rotation and orientation changes. 351 | * @param changed 352 | * @param left 353 | * @param top 354 | * @param right 355 | * @param bottom 356 | */ 357 | @Override 358 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) 359 | { 360 | // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails 361 | if (changed) { 362 | final int width = right - left; 363 | final int height = bottom - top; 364 | 365 | int previewWidth = width; 366 | int previewHeight = height; 367 | 368 | if (mPreviewSize != null){ 369 | Display display = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 370 | 371 | switch (display.getRotation()) 372 | { 373 | case Surface.ROTATION_0: 374 | previewWidth = mPreviewSize.height; 375 | previewHeight = mPreviewSize.width; 376 | mCamera.setDisplayOrientation(90); 377 | break; 378 | case Surface.ROTATION_90: 379 | previewWidth = mPreviewSize.width; 380 | previewHeight = mPreviewSize.height; 381 | break; 382 | case Surface.ROTATION_180: 383 | previewWidth = mPreviewSize.height; 384 | previewHeight = mPreviewSize.width; 385 | break; 386 | case Surface.ROTATION_270: 387 | previewWidth = mPreviewSize.width; 388 | previewHeight = mPreviewSize.height; 389 | mCamera.setDisplayOrientation(180); 390 | break; 391 | } 392 | } 393 | 394 | final int scaledChildHeight = previewHeight * width / previewWidth; 395 | mCameraView.layout(0, height - scaledChildHeight, width, height); 396 | } 397 | } 398 | 399 | /** 400 | * 401 | * @param sizes 402 | * @param width 403 | * @param height 404 | * @return 405 | */ 406 | private Camera.Size getOptimalPreviewSize(List sizes, int width, int height) 407 | { 408 | // Source: http://stackoverflow.com/questions/7942378/android-camera-will-not-work-startpreview-fails 409 | Camera.Size optimalSize = null; 410 | 411 | final double ASPECT_TOLERANCE = 0.1; 412 | double targetRatio = (double) height / width; 413 | 414 | // Try to find a size match which suits the whole screen minus the menu on the left. 415 | for (Camera.Size size : sizes){ 416 | 417 | if (size.height != width) continue; 418 | double ratio = (double) size.width / size.height; 419 | if (ratio <= targetRatio + ASPECT_TOLERANCE && ratio >= targetRatio - ASPECT_TOLERANCE){ 420 | optimalSize = size; 421 | } 422 | } 423 | 424 | // If we cannot find the one that matches the aspect ratio, ignore the requirement. 425 | if (optimalSize == null) { 426 | // TODO : Backup in case we don't get a size. 427 | } 428 | 429 | return optimalSize; 430 | } 431 | } 432 | 433 | /** 434 | * Picture Callback for handling a picture capture and saving it out to a file. 435 | */ 436 | private Camera.PictureCallback mPicture = new Camera.PictureCallback() { 437 | 438 | @Override 439 | public void onPictureTaken(byte[] data, Camera camera) { 440 | 441 | File pictureFile = getOutputMediaFile(); 442 | if (pictureFile == null){ 443 | Toast.makeText(getActivity(), "Image retrieval failed.", Toast.LENGTH_SHORT) 444 | .show(); 445 | return; 446 | } 447 | 448 | try { 449 | FileOutputStream fos = new FileOutputStream(pictureFile); 450 | fos.write(data); 451 | fos.close(); 452 | 453 | // Restart the camera preview. 454 | safeCameraOpenInView(mCameraView); 455 | } catch (FileNotFoundException e) { 456 | e.printStackTrace(); 457 | } catch (IOException e) { 458 | e.printStackTrace(); 459 | } 460 | } 461 | }; 462 | 463 | /** 464 | * Used to return the camera File output. 465 | * @return 466 | */ 467 | private File getOutputMediaFile(){ 468 | 469 | File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( 470 | Environment.DIRECTORY_PICTURES), "UltimateCameraGuideApp"); 471 | 472 | if (! mediaStorageDir.exists()){ 473 | if (! mediaStorageDir.mkdirs()){ 474 | Log.d("Camera Guide", "Required media storage does not exist"); 475 | return null; 476 | } 477 | } 478 | 479 | // Create a media file name 480 | String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 481 | File mediaFile; 482 | mediaFile = new File(mediaStorageDir.getPath() + File.separator + 483 | "IMG_"+ timeStamp + ".jpg"); 484 | 485 | DialogHelper.showDialog( "Success!","Your picture has been saved!",getActivity()); 486 | 487 | return mediaFile; 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/fragments/SimpleAndroidImagePickerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.fragments; 24 | 25 | import android.app.Activity; 26 | import android.content.Context; 27 | import android.content.Intent; 28 | import android.database.Cursor; 29 | import android.graphics.Bitmap; 30 | import android.graphics.BitmapFactory; 31 | import android.net.Uri; 32 | import android.os.Bundle; 33 | import android.provider.MediaStore; 34 | import android.view.LayoutInflater; 35 | import android.view.View; 36 | import android.view.ViewGroup; 37 | import android.widget.Button; 38 | import android.widget.ImageView; 39 | 40 | import com.ultimate.camera.R; 41 | import com.ultimate.camera.activities.MainActivity; 42 | 43 | /** 44 | * Example of loading an image into an image view using the image picker. 45 | * 46 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 47 | */ 48 | public class SimpleAndroidImagePickerFragment extends BaseFragment implements Button.OnClickListener { 49 | 50 | // Code for our image picker select action. 51 | private static final int IMAGE_PICKER_SELECT = 999; 52 | 53 | // Reference to our image view we will use 54 | private ImageView mSelectedImage; 55 | 56 | // Reference to picker button. 57 | private Button mPickPhotoButton; 58 | 59 | /** 60 | * Default empty constructor. 61 | */ 62 | public SimpleAndroidImagePickerFragment(){ 63 | super(); 64 | } 65 | 66 | /** 67 | * Static factory method 68 | * @param sectionNumber 69 | * @return 70 | */ 71 | public static SimpleAndroidImagePickerFragment newInstance(int sectionNumber) { 72 | SimpleAndroidImagePickerFragment fragment = new SimpleAndroidImagePickerFragment(); 73 | Bundle args = new Bundle(); 74 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 75 | fragment.setArguments(args); 76 | return fragment; 77 | } 78 | 79 | /** 80 | * OnCreateView fragment override 81 | * @param inflater 82 | * @param container 83 | * @param savedInstanceState 84 | * @return 85 | */ 86 | @Override 87 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 88 | Bundle savedInstanceState) { 89 | View view = null; 90 | view = inflater.inflate(R.layout.fragment_photo_picker, container, false); 91 | 92 | // Set the image view 93 | mSelectedImage = (ImageView)view.findViewById(R.id.imageViewFullSized); 94 | mPickPhotoButton = (Button)view.findViewById(R.id.button); 95 | 96 | // Set OnItemClickListener so we can be notified on button clicks 97 | mPickPhotoButton.setOnClickListener(this); 98 | 99 | return view; 100 | } 101 | 102 | @Override 103 | public void onClick(View view) { 104 | Intent i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); 105 | startActivityForResult(i, IMAGE_PICKER_SELECT); 106 | } 107 | 108 | /** 109 | * Photo Selection result 110 | */ 111 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 112 | if (requestCode == IMAGE_PICKER_SELECT && resultCode == Activity.RESULT_OK) { 113 | MainActivity activity = (MainActivity)getActivity(); 114 | Bitmap bitmap = getBitmapFromCameraData(data, activity); 115 | mSelectedImage.setImageBitmap(bitmap); 116 | } 117 | } 118 | 119 | /** 120 | * Scale the photo down and fit it to our image views. 121 | * 122 | * "Drastically increases performance" to set images using this technique. 123 | * Read more:http://developer.android.com/training/camera/photobasics.html 124 | */ 125 | private void setFullImageFromFilePath(String imagePath) { 126 | // Get the dimensions of the View 127 | int targetW = mSelectedImage.getWidth(); 128 | int targetH = mSelectedImage.getHeight(); 129 | 130 | // Get the dimensions of the bitmap 131 | BitmapFactory.Options bmOptions = new BitmapFactory.Options(); 132 | bmOptions.inJustDecodeBounds = true; 133 | BitmapFactory.decodeFile(imagePath, bmOptions); 134 | int photoW = bmOptions.outWidth; 135 | int photoH = bmOptions.outHeight; 136 | 137 | // Determine how much to scale down the image 138 | int scaleFactor = Math.min(photoW/targetW, photoH/targetH); 139 | 140 | // Decode the image file into a Bitmap sized to fill the View 141 | bmOptions.inJustDecodeBounds = false; 142 | bmOptions.inSampleSize = scaleFactor; 143 | bmOptions.inPurgeable = true; 144 | 145 | Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions); 146 | mSelectedImage.setImageBitmap(bitmap); 147 | } 148 | 149 | /** 150 | * Use for decoding camera response data. 151 | * 152 | * @param data 153 | * @param context 154 | * @return 155 | */ 156 | public static Bitmap getBitmapFromCameraData(Intent data, Context context){ 157 | Uri selectedImage = data.getData(); 158 | String[] filePathColumn = { MediaStore.Images.Media.DATA }; 159 | Cursor cursor = context.getContentResolver().query(selectedImage,filePathColumn, null, null, null); 160 | cursor.moveToFirst(); 161 | int columnIndex = cursor.getColumnIndex(filePathColumn[0]); 162 | String picturePath = cursor.getString(columnIndex); 163 | cursor.close(); 164 | return BitmapFactory.decodeFile(picturePath); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/fragments/SimpleCameraIntentFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.fragments; 24 | 25 | import android.app.Activity; 26 | import android.content.Context; 27 | import android.content.Intent; 28 | import android.content.pm.PackageManager; 29 | import android.graphics.Bitmap; 30 | import android.graphics.BitmapFactory; 31 | import android.net.Uri; 32 | import android.os.Bundle; 33 | import android.os.Environment; 34 | import android.provider.MediaStore; 35 | import android.view.LayoutInflater; 36 | import android.view.View; 37 | import android.view.ViewGroup; 38 | import android.widget.AbsListView; 39 | import android.widget.AdapterView; 40 | import android.widget.Button; 41 | import android.widget.ImageView; 42 | import android.widget.ListAdapter; 43 | import android.widget.RadioGroup; 44 | import android.widget.TextView; 45 | import android.widget.Toast; 46 | 47 | import com.ultimate.camera.R; 48 | import com.ultimate.camera.activities.CameraActivity; 49 | import com.ultimate.camera.utilities.ImageUtil; 50 | 51 | import java.io.File; 52 | import java.io.IOException; 53 | import java.text.SimpleDateFormat; 54 | import java.util.Date; 55 | 56 | /** 57 | * This is an example of a fragment which can use the external Android camera to take 58 | * a picture. It is important to remember to save the file URI where we want to save 59 | * our picture into the bundle because this data will be cleared when the camera is loaded. 60 | * The appropriate place to do this is in the Fragment's parent activity because there isn't a 61 | * good "entry" point when the fragment returns to the foreground to retrieve the bundle info. 62 | * 63 | * Reference: http://developer.android.com/training/camera/photobasics.html 64 | * 65 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 66 | */ 67 | public class SimpleCameraIntentFragment extends BaseFragment implements Button.OnClickListener { 68 | 69 | // Activity result key for camera 70 | static final int REQUEST_TAKE_PHOTO = 11111; 71 | 72 | // Image view for showing our image. 73 | private ImageView mImageView; 74 | private ImageView mThumbnailImageView; 75 | 76 | /** 77 | * Default empty constructor. 78 | */ 79 | public SimpleCameraIntentFragment(){ 80 | super(); 81 | } 82 | 83 | /** 84 | * Static factory method 85 | * @param sectionNumber 86 | * @return 87 | */ 88 | public static SimpleCameraIntentFragment newInstance(int sectionNumber) { 89 | SimpleCameraIntentFragment fragment = new SimpleCameraIntentFragment(); 90 | Bundle args = new Bundle(); 91 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 92 | fragment.setArguments(args); 93 | return fragment; 94 | } 95 | 96 | /** 97 | * OnCreateView fragment override 98 | * @param inflater 99 | * @param container 100 | * @param savedInstanceState 101 | * @return 102 | */ 103 | @Override 104 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 105 | Bundle savedInstanceState) { 106 | View view = null; 107 | view = inflater.inflate(R.layout.fragment_simple_camera_intent, container, false); 108 | 109 | // Set the image view 110 | mImageView = (ImageView)view.findViewById(R.id.imageViewFullSized); 111 | mThumbnailImageView = (ImageView)view.findViewById(R.id.imageViewThumbnail); 112 | Button takePictureButton = (Button)view.findViewById(R.id.button); 113 | 114 | // Set OnItemClickListener so we can be notified on button clicks 115 | takePictureButton.setOnClickListener(this); 116 | 117 | return view; 118 | } 119 | 120 | /** 121 | * Start the camera by dispatching a camera intent. 122 | */ 123 | protected void dispatchTakePictureIntent() { 124 | 125 | // Check if there is a camera. 126 | Context context = getActivity(); 127 | PackageManager packageManager = context.getPackageManager(); 128 | if(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) == false){ 129 | Toast.makeText(getActivity(), "This device does not have a camera.", Toast.LENGTH_SHORT) 130 | .show(); 131 | return; 132 | } 133 | 134 | // Camera exists? Then proceed... 135 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 136 | 137 | // Ensure that there's a camera activity to handle the intent 138 | CameraActivity activity = (CameraActivity)getActivity(); 139 | if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) { 140 | // Create the File where the photo should go. 141 | // If you don't do this, you may get a crash in some devices. 142 | File photoFile = null; 143 | try { 144 | photoFile = createImageFile(); 145 | } catch (IOException ex) { 146 | // Error occurred while creating the File 147 | Toast toast = Toast.makeText(activity, "There was a problem saving the photo...", Toast.LENGTH_SHORT); 148 | toast.show(); 149 | } 150 | // Continue only if the File was successfully created 151 | if (photoFile != null) { 152 | Uri fileUri = Uri.fromFile(photoFile); 153 | activity.setCapturedImageURI(fileUri); 154 | activity.setCurrentPhotoPath(fileUri.getPath()); 155 | takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, 156 | activity.getCapturedImageURI()); 157 | startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); 158 | } 159 | } 160 | } 161 | 162 | /** 163 | * The activity returns with the photo. 164 | * @param requestCode 165 | * @param resultCode 166 | * @param data 167 | */ 168 | @Override 169 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 170 | if (requestCode == REQUEST_TAKE_PHOTO && resultCode == Activity.RESULT_OK) { 171 | addPhotoToGallery(); 172 | CameraActivity activity = (CameraActivity)getActivity(); 173 | 174 | // Show the full sized image. 175 | setFullImageFromFilePath(activity.getCurrentPhotoPath(), mImageView); 176 | setFullImageFromFilePath(activity.getCurrentPhotoPath(), mThumbnailImageView); 177 | } else { 178 | Toast.makeText(getActivity(), "Image Capture Failed", Toast.LENGTH_SHORT) 179 | .show(); 180 | } 181 | } 182 | 183 | /** 184 | * Creates the image file to which the image must be saved. 185 | * @return 186 | * @throws IOException 187 | */ 188 | protected File createImageFile() throws IOException { 189 | // Create an image file name 190 | String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 191 | String imageFileName = "JPEG_" + timeStamp + "_"; 192 | File storageDir = Environment.getExternalStoragePublicDirectory( 193 | Environment.DIRECTORY_PICTURES); 194 | File image = File.createTempFile( 195 | imageFileName, /* prefix */ 196 | ".jpg", /* suffix */ 197 | storageDir /* directory */ 198 | ); 199 | 200 | // Save a file: path for use with ACTION_VIEW intents 201 | CameraActivity activity = (CameraActivity)getActivity(); 202 | activity.setCurrentPhotoPath("file:" + image.getAbsolutePath()); 203 | return image; 204 | } 205 | 206 | /** 207 | * Add the picture to the photo gallery. 208 | * Must be called on all camera images or they will 209 | * disappear once taken. 210 | */ 211 | protected void addPhotoToGallery() { 212 | Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 213 | CameraActivity activity = (CameraActivity)getActivity(); 214 | File f = new File(activity.getCurrentPhotoPath()); 215 | Uri contentUri = Uri.fromFile(f); 216 | mediaScanIntent.setData(contentUri); 217 | this.getActivity().sendBroadcast(mediaScanIntent); 218 | } 219 | 220 | /** 221 | * Deal with button clicks. 222 | * @param v 223 | */ 224 | @Override 225 | public void onClick(View v) { 226 | dispatchTakePictureIntent(); 227 | } 228 | 229 | /** 230 | * Scale the photo down and fit it to our image views. 231 | * 232 | * "Drastically increases performance" to set images using this technique. 233 | * Read more:http://developer.android.com/training/camera/photobasics.html 234 | */ 235 | private void setFullImageFromFilePath(String imagePath, ImageView imageView) { 236 | // Get the dimensions of the View 237 | int targetW = imageView.getWidth(); 238 | int targetH = imageView.getHeight(); 239 | 240 | // Get the dimensions of the bitmap 241 | BitmapFactory.Options bmOptions = new BitmapFactory.Options(); 242 | bmOptions.inJustDecodeBounds = true; 243 | BitmapFactory.decodeFile(imagePath, bmOptions); 244 | int photoW = bmOptions.outWidth; 245 | int photoH = bmOptions.outHeight; 246 | 247 | // Determine how much to scale down the image 248 | int scaleFactor = Math.min(photoW/targetW, photoH/targetH); 249 | 250 | // Decode the image file into a Bitmap sized to fill the View 251 | bmOptions.inJustDecodeBounds = false; 252 | bmOptions.inSampleSize = scaleFactor; 253 | bmOptions.inPurgeable = true; 254 | 255 | Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions); 256 | imageView.setImageBitmap(bitmap); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/fragments/SimplePhotoGalleryListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.fragments; 24 | 25 | import android.app.Activity; 26 | import android.app.LoaderManager; 27 | import android.app.ProgressDialog; 28 | import android.content.Context; 29 | import android.content.Loader; 30 | import android.os.Bundle; 31 | import android.view.LayoutInflater; 32 | import android.view.View; 33 | import android.view.ViewGroup; 34 | import android.widget.AbsListView; 35 | import android.widget.AdapterView; 36 | import android.widget.BaseAdapter; 37 | import android.widget.ListAdapter; 38 | import android.widget.TextView; 39 | 40 | import com.ultimate.camera.R; 41 | import com.ultimate.camera.activities.MainActivity; 42 | import com.ultimate.camera.adapters.PhotoAdapter; 43 | import com.ultimate.camera.adapters.items.PhotoItem; 44 | import com.ultimate.camera.utilities.PhotoGalleryAsyncLoader; 45 | 46 | import java.util.ArrayList; 47 | import java.util.Iterator; 48 | import java.util.List; 49 | 50 | /** 51 | * This is an example which will load all the images on your phone into a grid using a background 52 | * image AsyncLoader. 53 | * 54 | * Reference: http://developer.android.com/reference/android/content/AsyncTaskLoader.html 55 | * 56 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 57 | */ 58 | public class SimplePhotoGalleryListFragment extends BaseFragment implements AbsListView.OnItemClickListener, 59 | LoaderManager.LoaderCallbacks> { 60 | 61 | // Ivars. 62 | protected OnFragmentInteractionListener mListener; 63 | protected AbsListView mListView; 64 | protected PhotoAdapter mAdapter; 65 | protected ArrayList mPhotoListItem; 66 | protected TextView mEmptyTextView; 67 | protected ProgressDialog mLoadingProgressDialog; 68 | 69 | /** 70 | * Required empty constructor 71 | */ 72 | public SimplePhotoGalleryListFragment() { 73 | super(); 74 | } 75 | 76 | /** 77 | * Static factory method 78 | * @param sectionNumber 79 | * @return 80 | */ 81 | public static SimplePhotoGalleryListFragment newInstance(int sectionNumber) { 82 | SimplePhotoGalleryListFragment fragment = new SimplePhotoGalleryListFragment(); 83 | Bundle args = new Bundle(); 84 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 85 | fragment.setArguments(args); 86 | return fragment; 87 | } 88 | 89 | @Override 90 | public void onCreate(Bundle savedInstanceState) { 91 | super.onCreate(savedInstanceState); 92 | 93 | // Create an empty loader and pre-initialize the photo list items as an empty list. 94 | Context context = getActivity().getBaseContext(); 95 | 96 | // Set up empty mAdapter 97 | mPhotoListItem = new ArrayList() ; 98 | mAdapter = new PhotoAdapter(context, 99 | R.layout.photo_item, 100 | mPhotoListItem, false); 101 | 102 | // Prepare the loader. Either re-connect with an existing one, 103 | // or start a new one. 104 | getLoaderManager().initLoader(0, null, this); 105 | } 106 | 107 | @Override 108 | public View onCreateView(LayoutInflater inflater, 109 | ViewGroup container, 110 | Bundle savedInstanceState) { 111 | View view = null; 112 | view = inflater.inflate(R.layout.fragment_photo_gallery, container, false); 113 | 114 | // Set the mAdapter 115 | mListView = (AbsListView) view.findViewById(android.R.id.list); 116 | ((AdapterView) mListView).setAdapter(mAdapter); 117 | mEmptyTextView = (TextView)view.findViewById(R.id.empty); 118 | 119 | // Show the empty text / message. 120 | resolveEmptyText(); 121 | 122 | // Set OnItemClickListener so we can be notified on item clicks 123 | mListView.setOnItemClickListener(this); 124 | 125 | return view; 126 | } 127 | 128 | /** 129 | * Used to show a generic empty text warning. Override in inheriting classes. 130 | */ 131 | protected void resolveEmptyText(){ 132 | if(mAdapter.isEmpty()){ 133 | mEmptyTextView.setVisibility(View.VISIBLE); 134 | setEmptyText(); 135 | } else { 136 | mEmptyTextView.setVisibility(View.INVISIBLE); 137 | } 138 | } 139 | 140 | @Override 141 | public void onAttach(Activity activity) { 142 | super.onAttach(activity); 143 | try { 144 | mListener = (OnFragmentInteractionListener) activity; 145 | // Show a progress dialog. 146 | mLoadingProgressDialog = new ProgressDialog(getActivity()); 147 | mLoadingProgressDialog.setMessage("Loading Photos..."); 148 | mLoadingProgressDialog.setCancelable(true); 149 | mLoadingProgressDialog.show(); 150 | } catch (ClassCastException e) { 151 | throw new ClassCastException(activity.toString() 152 | + " must implement OnFragmentInteractionListener"); 153 | } 154 | } 155 | 156 | @Override 157 | public void onDetach() { 158 | super.onDetach(); 159 | mListener = null; 160 | cancelProgressDialog(); 161 | } 162 | 163 | @Override 164 | public void onPause(){ 165 | super.onPause(); 166 | cancelProgressDialog(); 167 | } 168 | 169 | @Override 170 | public void onStop(){ 171 | super.onStop(); 172 | cancelProgressDialog(); 173 | } 174 | 175 | /** 176 | * This is only triggered when the user selects a single photo. 177 | * @param parent 178 | * @param view 179 | * @param position 180 | * @param id 181 | */ 182 | 183 | @Override 184 | public void onItemClick(AdapterView parent, View view, int position, long id) { 185 | if (null != mListener) { 186 | // Tell the share builder to add the photo to the share operation. 187 | PhotoItem photoListItem = (PhotoItem)this.mAdapter.getItem(position); 188 | String imagePath = photoListItem.getThumbnailUri().getPath(); 189 | mListener.onFragmentInteraction(MainActivity.SELECT_PHOTO_ACTION); 190 | resetFragmentState(); 191 | } 192 | } 193 | 194 | /** 195 | * Used when hitting the back button to reset the mFragment UI state 196 | */ 197 | protected void resetFragmentState(){ 198 | // Clear view state 199 | getActivity().invalidateOptionsMenu(); 200 | ((BaseAdapter) mListView.getAdapter()).notifyDataSetChanged(); 201 | } 202 | 203 | /** 204 | * The default content for this Fragment has a TextView that is shown when 205 | * the list is empty. If you would like to change the text, call this method 206 | * to supply the text it should use. 207 | */ 208 | public void setEmptyText() { 209 | mEmptyTextView.setText("No Photos!"); 210 | } 211 | 212 | /** 213 | * Loader Handlers for loading the photos in the background. 214 | */ 215 | @Override 216 | public Loader> onCreateLoader(int id, Bundle args) { 217 | // This is called when a new Loader needs to be created. This 218 | // sample only has one Loader with no arguments, so it is simple. 219 | return new PhotoGalleryAsyncLoader(getActivity()); 220 | } 221 | 222 | @Override 223 | public void onLoadFinished(Loader> loader, List data) { 224 | // Set the new data in the mAdapter. 225 | mPhotoListItem.clear(); 226 | 227 | for(int i = 0; i < data.size();i++){ 228 | PhotoItem item = data.get(i); 229 | mPhotoListItem.add(item); 230 | } 231 | 232 | mAdapter.notifyDataSetChanged(); 233 | resolveEmptyText(); 234 | cancelProgressDialog(); 235 | } 236 | 237 | @Override 238 | public void onLoaderReset(Loader> loader) { 239 | // Clear the data in the mAdapter. 240 | mPhotoListItem.clear(); 241 | mAdapter.notifyDataSetChanged(); 242 | resolveEmptyText(); 243 | cancelProgressDialog(); 244 | } 245 | 246 | /** 247 | * Save cancel for the progress loader 248 | */ 249 | private void cancelProgressDialog(){ 250 | if(mLoadingProgressDialog != null){ 251 | if(mLoadingProgressDialog.isShowing()){ 252 | mLoadingProgressDialog.cancel(); 253 | } 254 | } 255 | } 256 | } -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/utilities/DeviceInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.utilities; 24 | 25 | import android.bluetooth.BluetoothAdapter; 26 | import android.os.Build; 27 | 28 | /** 29 | * Utility for fetching details about the device. 30 | * 31 | * Created by Rex St. John (on behalf of AirPair.com) on 3/6/14. 32 | */ 33 | public class DeviceInfo { 34 | 35 | /** 36 | * Get the device's manufacturer name string. 37 | * @return 38 | */ 39 | public static String getDeviceName() { 40 | String manufacturer = Build.MANUFACTURER; 41 | String model = Build.MODEL; 42 | if (model.startsWith(manufacturer)) { 43 | return capitalize(model); 44 | } else { 45 | return capitalize(manufacturer) + " " + model; 46 | } 47 | } 48 | 49 | /** 50 | * Get the device's UUID 51 | * @return 52 | */ 53 | public static String getDeviceUUID(){ 54 | BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 55 | String uuid = adapter.getAddress(); 56 | return uuid; 57 | } 58 | 59 | /** 60 | * Internal helper to capitalize the string properly. 61 | * @param s 62 | * @return 63 | */ 64 | private static String capitalize(String s) { 65 | if (s == null || s.length() == 0) { 66 | return ""; 67 | } 68 | char first = s.charAt(0); 69 | if (Character.isUpperCase(first)) { 70 | return s; 71 | } else { 72 | return Character.toUpperCase(first) + s.substring(1); 73 | } 74 | } 75 | 76 | /** 77 | * Is this application an emulator? 78 | * @return 79 | */ 80 | public static boolean isEmulator(){ 81 | String brand = Build.BRAND; 82 | if (brand.equalsIgnoreCase("generic")) { 83 | return true; 84 | } else { 85 | return false; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/utilities/DialogHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.utilities; 24 | 25 | import android.app.AlertDialog; 26 | import android.content.Context; 27 | import android.content.DialogInterface; 28 | 29 | /** 30 | * Created by Rex St. John (on behalf of AirPair.com) on 3/6/14. 31 | */ 32 | public class DialogHelper { 33 | 34 | /** 35 | * Generic error dialog with a close button. 36 | * @param title 37 | * @param message 38 | * @param context 39 | */ 40 | public static void showDialog(String title, String message, Context context){ 41 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( 42 | context); 43 | alertDialogBuilder.setTitle(title); 44 | alertDialogBuilder 45 | .setMessage(message) 46 | .setCancelable(false) 47 | .setPositiveButton("Close",new DialogInterface.OnClickListener() { 48 | public void onClick(DialogInterface dialog,int id) { 49 | dialog.cancel(); 50 | } 51 | }); 52 | AlertDialog alertDialog = alertDialogBuilder.create(); 53 | alertDialog.show(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/utilities/ImageUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.utilities; 24 | 25 | import android.content.Context; 26 | import android.content.Intent; 27 | import android.database.Cursor; 28 | import android.graphics.Bitmap; 29 | import android.graphics.BitmapFactory; 30 | import android.net.Uri; 31 | import android.provider.MediaStore; 32 | 33 | /** 34 | * Created by Rex St. John (on behalf of AirPair.com) on 3/6/14. 35 | */ 36 | public class ImageUtil { 37 | 38 | /** 39 | * Get thumbnail as a bitmap from a path given a resolution 40 | * @param path 41 | * @param thumbnailSize 42 | * @return 43 | */ 44 | public static Bitmap getThumbnailBitmap(String path, int thumbnailSize) { 45 | BitmapFactory.Options bounds = new BitmapFactory.Options(); 46 | bounds.inJustDecodeBounds = true; 47 | BitmapFactory.decodeFile(path, bounds); 48 | if ((bounds.outWidth == -1) || (bounds.outHeight == -1)) { 49 | return null; 50 | } 51 | int originalSize = (bounds.outHeight > bounds.outWidth) ? bounds.outHeight 52 | : bounds.outWidth; 53 | BitmapFactory.Options opts = new BitmapFactory.Options(); 54 | opts.inSampleSize = originalSize / thumbnailSize; 55 | return BitmapFactory.decodeFile(path, opts); 56 | } 57 | 58 | /** 59 | * Use for decoding camera response data. 60 | * 61 | * Example call: 62 | * Intent i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); 63 | startActivityForResult(i, R.integer.PHOTO_SELECT_ACTION); 64 | * 65 | * @param data 66 | * @param context 67 | * @return 68 | */ 69 | public static Bitmap getBitmapFromCameraData(Intent data, Context context){ 70 | Uri selectedImage = data.getData(); 71 | String[] filePathColumn = { MediaStore.Images.Media.DATA }; 72 | Cursor cursor = context.getContentResolver().query(selectedImage,filePathColumn, null, null, null); 73 | cursor.moveToFirst(); 74 | int columnIndex = cursor.getColumnIndex(filePathColumn[0]); 75 | String picturePath = cursor.getString(columnIndex); 76 | cursor.close(); 77 | return BitmapFactory.decodeFile(picturePath); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/utilities/PhotoGalleryAsyncLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.utilities; 24 | 25 | import android.content.AsyncTaskLoader; 26 | import android.content.Context; 27 | import android.os.Environment; 28 | 29 | import java.util.List; 30 | 31 | import android.content.AsyncTaskLoader; 32 | import android.content.Context; 33 | 34 | import com.ultimate.camera.adapters.items.PhotoItem; 35 | 36 | import java.util.List; 37 | 38 | /** 39 | * AsyncTask loader used to load image resources in the background of a fragment. 40 | * Reference: http://developer.android.com/reference/android/content/AsyncTaskLoader.html 41 | * 42 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 43 | */ 44 | public class PhotoGalleryAsyncLoader extends AsyncTaskLoader> { 45 | 46 | // Persistent list of photo list item 47 | private List mPhotoListItems; 48 | 49 | /** 50 | * Constructor 51 | * @param context 52 | */ 53 | public PhotoGalleryAsyncLoader(Context context) { 54 | super(context); 55 | } 56 | 57 | /** 58 | * Matches code in MediaProvider.computeBucketValues. 59 | */ 60 | public static String getBucketId(String path) { 61 | return String.valueOf(path.toLowerCase().hashCode()); 62 | } 63 | 64 | /** 65 | * Load photo album image items in the background 66 | * 67 | * This is where the bulk of our work is done. This function is 68 | * called in a background thread and should generate a new set of 69 | * data to be published by the loader. 70 | */ 71 | @Override 72 | public List loadInBackground() { 73 | final Context context = getContext(); 74 | List photos = PhotoGalleryImageProvider.getAlbumThumbnails(context); 75 | return photos; 76 | } 77 | 78 | /** 79 | * Called when there is new data to deliver to the client. The 80 | * super class will take care of delivering it; the implementation 81 | * here just adds a little more logic. 82 | */ 83 | @Override public void deliverResult(List newPhotoListItems) { 84 | if (isReset()) { 85 | // An async query came in while the loader is stopped. We 86 | // don't need the result. 87 | if (newPhotoListItems != null) { 88 | onReleaseResources(newPhotoListItems); 89 | } 90 | } 91 | List oldPhotos = mPhotoListItems; 92 | mPhotoListItems = newPhotoListItems; 93 | 94 | if (isStarted()) { 95 | // If the Loader is currently started, we can immediately 96 | // deliver its results. 97 | super.deliverResult(newPhotoListItems); 98 | } 99 | 100 | // At this point we can release the resources associated with 101 | // 'oldApps' if needed; now that the new result is delivered we 102 | // know that it is no longer in use. 103 | if (oldPhotos != null) { 104 | onReleaseResources(oldPhotos); 105 | } 106 | } 107 | 108 | /** 109 | * Handles a request to start the Loader. 110 | */ 111 | @Override protected void onStartLoading() { 112 | 113 | if (mPhotoListItems != null) { 114 | // If we currently have a result available, deliver it 115 | // immediately. 116 | deliverResult(mPhotoListItems); 117 | } else { 118 | // If the data has changed since the last time it was loaded 119 | // or is not currently available, start a load. 120 | forceLoad(); 121 | } 122 | } 123 | 124 | /** 125 | * Handles a request to stop the Loader. 126 | */ 127 | @Override protected void onStopLoading() { 128 | // Attempt to cancel the current load task if possible. 129 | cancelLoad(); 130 | } 131 | 132 | /** 133 | * Handles a request to cancel a load. 134 | */ 135 | @Override public void onCanceled(List photoListItems) { 136 | super.onCanceled(photoListItems); 137 | 138 | // At this point we can release the resources associated with 'apps' 139 | // if needed. 140 | onReleaseResources(photoListItems); 141 | } 142 | 143 | /** 144 | * Handles a request to completely reset the Loader. 145 | */ 146 | @Override protected void onReset() { 147 | super.onReset(); 148 | 149 | // Ensure the loader is stopped 150 | onStopLoading(); 151 | 152 | // At this point we can release the resources associated with 'apps' 153 | // if needed. 154 | if (mPhotoListItems != null) { 155 | onReleaseResources(mPhotoListItems); 156 | mPhotoListItems = null; 157 | } 158 | } 159 | 160 | /** 161 | * Helper function to take care of releasing resources associated 162 | * with an actively loaded data set. 163 | */ 164 | protected void onReleaseResources(List photoListItems) { 165 | // For a simple List<> there is nothing to do. For something 166 | // like a Cursor, we would close it here. 167 | } 168 | } -------------------------------------------------------------------------------- /camera/src/main/java/com/ultimate/camera/utilities/PhotoGalleryImageProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Rex St. John on behalf of AirPair.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.ultimate.camera.utilities; 24 | 25 | import android.content.Context; 26 | import android.content.CursorLoader; 27 | import android.database.Cursor; 28 | import android.graphics.Bitmap; 29 | import android.graphics.BitmapFactory; 30 | import android.net.Uri; 31 | import android.os.Environment; 32 | import android.provider.MediaStore; 33 | 34 | import com.ultimate.camera.adapters.items.PhotoItem; 35 | 36 | import java.io.File; 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | /** 41 | * This is a helper utility which automatically fetches paths to full size and thumbnail sized gallery images. 42 | * 43 | * Created by Rex St. John (on behalf of AirPair.com) on 3/4/14. 44 | */ 45 | public class PhotoGalleryImageProvider { 46 | 47 | // Consts 48 | public static final int IMAGE_RESOLUTION = 15; 49 | 50 | // Buckets where we are fetching images from. 51 | public static final String CAMERA_IMAGE_BUCKET_NAME = 52 | Environment.getExternalStorageDirectory().toString() 53 | + "/DCIM/Camera"; 54 | public static final String CAMERA_IMAGE_BUCKET_ID = 55 | getBucketId(CAMERA_IMAGE_BUCKET_NAME); 56 | 57 | /** 58 | * Fetch both full sized images and thumbnails via a single query. 59 | * Returns all images not in the Camera Roll. 60 | * @param context 61 | * @return 62 | */ 63 | public static List getAlbumThumbnails(Context context){ 64 | 65 | final String[] projection = {MediaStore.Images.Thumbnails.DATA,MediaStore.Images.Thumbnails.IMAGE_ID}; 66 | 67 | Cursor thumbnailsCursor = context.getContentResolver().query( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, 68 | projection, // Which columns to return 69 | null, // Return all rows 70 | null, 71 | null); 72 | 73 | // Extract the proper column thumbnails 74 | int thumbnailColumnIndex = thumbnailsCursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA); 75 | ArrayList result = new ArrayList(thumbnailsCursor.getCount()); 76 | 77 | if (thumbnailsCursor.moveToFirst()) { 78 | do { 79 | // Generate a tiny thumbnail version. 80 | int thumbnailImageID = thumbnailsCursor.getInt(thumbnailColumnIndex); 81 | String thumbnailPath = thumbnailsCursor.getString(thumbnailImageID); 82 | Uri thumbnailUri = Uri.parse(thumbnailPath); 83 | Uri fullImageUri = uriToFullImage(thumbnailsCursor,context); 84 | 85 | // Create the list item. 86 | PhotoItem newItem = new PhotoItem(thumbnailUri,fullImageUri); 87 | result.add(newItem); 88 | } while (thumbnailsCursor.moveToNext()); 89 | } 90 | thumbnailsCursor.close(); 91 | return result; 92 | } 93 | 94 | /** 95 | * Get the path to the full image for a given thumbnail. 96 | */ 97 | private static Uri uriToFullImage(Cursor thumbnailsCursor, Context context){ 98 | String imageId = thumbnailsCursor.getString(thumbnailsCursor.getColumnIndex(MediaStore.Images.Thumbnails.IMAGE_ID)); 99 | 100 | // Request image related to this thumbnail 101 | String[] filePathColumn = { MediaStore.Images.Media.DATA }; 102 | Cursor imagesCursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, filePathColumn, MediaStore.Images.Media._ID + "=?", new String[]{imageId}, null); 103 | 104 | if (imagesCursor != null && imagesCursor.moveToFirst()) { 105 | int columnIndex = imagesCursor.getColumnIndex(filePathColumn[0]); 106 | String filePath = imagesCursor.getString(columnIndex); 107 | imagesCursor.close(); 108 | return Uri.parse(filePath); 109 | } else { 110 | imagesCursor.close(); 111 | return Uri.parse(""); 112 | } 113 | } 114 | 115 | /** 116 | * Render a thumbnail photo and scale it down to a smaller size. 117 | * @param path 118 | * @return 119 | */ 120 | private static Bitmap bitmapFromPath(String path){ 121 | File imgFile = new File(path); 122 | Bitmap imageBitmap = null; 123 | 124 | if(imgFile.exists()){ 125 | BitmapFactory.Options options = new BitmapFactory.Options(); 126 | options.inSampleSize = IMAGE_RESOLUTION; 127 | imageBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), options); 128 | } 129 | return imageBitmap; 130 | } 131 | 132 | /** 133 | * Matches code in MediaProvider.computeBucketValues. Should be a common 134 | * function. 135 | */ 136 | public static String getBucketId(String path) { 137 | return String.valueOf(path.toLowerCase().hashCode()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /camera/src/main/res/drawable-hdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-hdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-hdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-hdpi/ic_drawer.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-mdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-mdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-mdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-mdpi/ic_drawer.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-xhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-xhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-xhdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-xhdpi/ic_drawer.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-xxhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-xxhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-xxhdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-xxhdpi/ic_drawer.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /camera/src/main/res/drawable/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rexstjohn/UltimateAndroidCameraGuide/f0e00202c1c9f299e958a8ea295ebd21f5d10574/camera/src/main/res/drawable/placeholder.png -------------------------------------------------------------------------------- /camera/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 31 | 32 | 34 | 38 | 39 | 44 | 46 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /camera/src/main/res/layout/fragment_horizontal_gallery.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 | 28 | 29 | 39 | 45 | -------------------------------------------------------------------------------- /camera/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 32 | 33 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /camera/src/main/res/layout/fragment_native_camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 | 30 | 36 | 37 | 38 | 43 |