├── .gitignore ├── CriminalIntent.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bignerdranch │ │ └── android │ │ └── criminalintent │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bignerdranch │ │ └── android │ │ └── criminalintent │ │ ├── Crime.java │ │ ├── CrimeFragment.java │ │ ├── CrimeLab.java │ │ ├── CrimeListActivity.java │ │ ├── CrimeListFragment.java │ │ ├── CrimePagerActivity.java │ │ ├── DatePickerFragment.java │ │ ├── PictureUtils.java │ │ ├── SingleFragmentActivity.java │ │ └── database │ │ ├── CrimeBaseHelper.java │ │ ├── CrimeCursorWrapper.java │ │ └── CrimeDbSchema.java │ └── res │ ├── drawable-hdpi │ ├── ic_add_white_24dp.png │ └── ic_photo_camera_white_24dp.png │ ├── drawable-mdpi │ ├── ic_add_white_24dp.png │ └── ic_photo_camera_white_24dp.png │ ├── drawable-xhdpi │ ├── ic_add_white_24dp.png │ └── ic_photo_camera_white_24dp.png │ ├── drawable-xxhdpi │ ├── ic_add_white_24dp.png │ └── ic_photo_camera_white_24dp.png │ ├── drawable-xxxhdpi │ ├── ic_add_white_24dp.png │ └── ic_photo_camera_white_24dp.png │ ├── layout-land │ └── fragment_crime.xml │ ├── layout │ ├── activity_crime_pager.xml │ ├── activity_fragment.xml │ ├── activity_twopane.xml │ ├── dialog_date.xml │ ├── fragment_crime.xml │ ├── fragment_crime_list.xml │ ├── list_item_crime.xml │ └── view_camera_and_title.xml │ ├── menu │ └── crime.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-sw600dp │ └── refs.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── refs.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | -------------------------------------------------------------------------------- /CriminalIntent.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Design Support Library Demo 2 | 3 | This project demos some of the new toys in the Design Support Library. 4 | The starting point of this project is the solution for the Criminal Intent project from the Big Nerd Ranch [Android Programming](https://training.bignerdranch.com/classes/android-bootcamp) course. 5 | Please read the [blog post](https://www.bignerdranch.com/blog/becoming-material-with-android-design-support-library/) that describes the goodies. 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.bignerdranch.android.criminalintent" 9 | minSdkVersion 16 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | 16 | } 17 | } 18 | 19 | dependencies { 20 | compile fileTree(dir: 'libs', include: ['*.jar']) 21 | compile 'com.android.support:support-v4:22.2.0' 22 | compile 'com.android.support:recyclerview-v7:22.2.0' 23 | compile 'com.android.support:appcompat-v7:22.2.0' 24 | compile 'com.android.support:design:22.2.0' 25 | } 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /AndroidDeveloper/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bignerdranch/android/criminalintent/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 11 | 12 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import java.util.Date; 4 | import java.util.UUID; 5 | 6 | public class Crime { 7 | 8 | private UUID mId; 9 | private String mTitle; 10 | private Date mDate; 11 | private boolean mSolved; 12 | private String mSuspect; 13 | 14 | public Crime() { 15 | this(UUID.randomUUID()); 16 | } 17 | 18 | public Crime(UUID id) { 19 | mId = id; 20 | mDate = new Date(); 21 | } 22 | public UUID getId() { 23 | return mId; 24 | } 25 | 26 | public String getTitle() { 27 | return mTitle; 28 | } 29 | 30 | public void setTitle(String title) { 31 | mTitle = title; 32 | } 33 | 34 | public Date getDate() { 35 | return mDate; 36 | } 37 | 38 | public void setDate(Date date) { 39 | mDate = date; 40 | } 41 | 42 | public boolean isSolved() { 43 | return mSolved; 44 | } 45 | 46 | public void setSolved(boolean solved) { 47 | mSolved = solved; 48 | } 49 | 50 | public String getSuspect() { 51 | return mSuspect; 52 | } 53 | 54 | public void setSuspect(String suspect) { 55 | mSuspect = suspect; 56 | } 57 | 58 | public String getPhotoFilename() { 59 | return "IMG_" + getId().toString() + ".jpg"; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.app.Activity; 4 | import android.content.ContentResolver; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.database.Cursor; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.provider.ContactsContract; 11 | import android.support.design.widget.Snackbar; 12 | import android.support.v4.app.Fragment; 13 | import android.support.v4.app.FragmentManager; 14 | import android.text.Editable; 15 | import android.text.TextUtils; 16 | import android.text.TextWatcher; 17 | import android.text.format.DateFormat; 18 | import android.view.LayoutInflater; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.Button; 22 | import android.widget.CheckBox; 23 | import android.widget.CompoundButton; 24 | import android.widget.CompoundButton.OnCheckedChangeListener; 25 | import android.widget.EditText; 26 | import android.widget.ImageButton; 27 | 28 | import java.io.File; 29 | import java.util.Date; 30 | import java.util.UUID; 31 | 32 | public class CrimeFragment extends Fragment { 33 | 34 | private static final String ARG_CRIME_ID = "crime_id"; 35 | private static final String DIALOG_DATE = "DialogDate"; 36 | 37 | private static final int REQUEST_DATE = 0; 38 | private static final int REQUEST_CONTACT = 1; 39 | private static final int REQUEST_PHOTO= 2; 40 | 41 | private Crime mCrime; 42 | private File mPhotoFile; 43 | private EditText mTitleField; 44 | private Button mDateButton; 45 | private CheckBox mSolvedCheckbox; 46 | private Button mReportButton; 47 | private Button mSuspectButton; 48 | private ImageButton mPhotoButton; 49 | private Callbacks mCallbacks; 50 | 51 | /** 52 | * Required interface for hosting activities. 53 | */ 54 | public interface Callbacks { 55 | void onCrimeUpdated(Crime crime); 56 | } 57 | 58 | public static CrimeFragment newInstance(UUID crimeId) { 59 | Bundle args = new Bundle(); 60 | args.putSerializable(ARG_CRIME_ID, crimeId); 61 | 62 | CrimeFragment fragment = new CrimeFragment(); 63 | fragment.setArguments(args); 64 | return fragment; 65 | } 66 | 67 | @Override 68 | public void onAttach(Activity activity) { 69 | super.onAttach(activity); 70 | mCallbacks = (Callbacks)activity; 71 | } 72 | 73 | @Override 74 | public void onCreate(Bundle savedInstanceState) { 75 | super.onCreate(savedInstanceState); 76 | UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID); 77 | mCrime = CrimeLab.get(getActivity()).getCrime(crimeId); 78 | } 79 | 80 | @Override 81 | public void onPause() { 82 | super.onPause(); 83 | 84 | CrimeLab.get(getActivity()) 85 | .updateCrime(mCrime); 86 | } 87 | 88 | @Override 89 | public void onDetach() { 90 | super.onDetach(); 91 | mCallbacks = null; 92 | } 93 | 94 | @Override 95 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 96 | Bundle savedInstanceState) { 97 | View v = inflater.inflate(R.layout.fragment_crime, container, false); 98 | 99 | mTitleField = (EditText) v.findViewById(R.id.crime_title); 100 | if (!TextUtils.isEmpty(mCrime.getTitle())) { 101 | mTitleField.setText(mCrime.getTitle()); 102 | } 103 | mTitleField.addTextChangedListener(new TextWatcher() { 104 | @Override 105 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 106 | 107 | } 108 | 109 | @Override 110 | public void onTextChanged(CharSequence s, int start, int before, int count) { 111 | if (getActivity() == null) { 112 | return; 113 | } 114 | mCrime.setTitle(s.toString()); 115 | updateCrime(); 116 | } 117 | 118 | @Override 119 | public void afterTextChanged(Editable s) { 120 | 121 | } 122 | }); 123 | 124 | mDateButton = (Button) v.findViewById(R.id.crime_date); 125 | updateDate(); 126 | mDateButton.setOnClickListener(new View.OnClickListener() { 127 | @Override 128 | public void onClick(View v) { 129 | FragmentManager manager = getFragmentManager(); 130 | DatePickerFragment dialog = DatePickerFragment 131 | .newInstance(mCrime.getDate()); 132 | dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE); 133 | dialog.show(manager, DIALOG_DATE); 134 | } 135 | }); 136 | 137 | mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved); 138 | mSolvedCheckbox.setChecked(mCrime.isSolved()); 139 | mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 140 | @Override 141 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 142 | mCrime.setSolved(isChecked); 143 | updateCrime(); 144 | if (isChecked) { 145 | Snackbar.make(getView(), "Solved the crime", Snackbar.LENGTH_LONG).setAction("Undo", new View.OnClickListener() { 146 | @Override public void onClick(View v) { 147 | mSolvedCheckbox.setChecked(false); 148 | } 149 | }).show(); 150 | } 151 | } 152 | }); 153 | 154 | mReportButton = (Button)v.findViewById(R.id.crime_report); 155 | mReportButton.setOnClickListener(new View.OnClickListener() { 156 | public void onClick(View v) { 157 | Intent i = new Intent(Intent.ACTION_SEND); 158 | i.setType("text/plain"); 159 | i.putExtra(Intent.EXTRA_TEXT, getCrimeReport()); 160 | i.putExtra(Intent.EXTRA_SUBJECT, 161 | getString(R.string.crime_report_subject)); 162 | i = Intent.createChooser(i, getString(R.string.send_report)); 163 | 164 | startActivity(i); 165 | } 166 | }); 167 | 168 | final Intent pickContact = new Intent(Intent.ACTION_PICK, 169 | ContactsContract.Contacts.CONTENT_URI); 170 | mSuspectButton = (Button)v.findViewById(R.id.crime_suspect); 171 | mSuspectButton.setOnClickListener(new View.OnClickListener() { 172 | public void onClick(View v) { 173 | startActivityForResult(pickContact, REQUEST_CONTACT); 174 | } 175 | }); 176 | 177 | if (mCrime.getSuspect() != null) { 178 | mSuspectButton.setText(mCrime.getSuspect()); 179 | } 180 | 181 | PackageManager packageManager = getActivity().getPackageManager(); 182 | if (packageManager.resolveActivity(pickContact, 183 | PackageManager.MATCH_DEFAULT_ONLY) == null) { 184 | mSuspectButton.setEnabled(false); 185 | } 186 | 187 | return v; 188 | } 189 | 190 | @Override 191 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 192 | if (resultCode != Activity.RESULT_OK) { 193 | return; 194 | } 195 | 196 | if (requestCode == REQUEST_DATE) { 197 | Date date = (Date) data 198 | .getSerializableExtra(DatePickerFragment.EXTRA_DATE); 199 | mCrime.setDate(date); 200 | updateCrime(); 201 | updateDate(); 202 | } else if (requestCode == REQUEST_CONTACT && data != null) { 203 | Uri contactUri = data.getData(); 204 | // Specify which fields you want your query to return 205 | // values for. 206 | String[] queryFields = new String[] { 207 | ContactsContract.Contacts.DISPLAY_NAME, 208 | }; 209 | // Perform your query - the contactUri is like a "where" 210 | // clause here 211 | ContentResolver resolver = getActivity().getContentResolver(); 212 | Cursor c = resolver 213 | .query(contactUri, queryFields, null, null, null); 214 | 215 | try { 216 | // Double-check that you actually got results 217 | if (c.getCount() == 0) { 218 | return; 219 | } 220 | 221 | // Pull out the first column of the first row of data - 222 | // that is your suspect's name. 223 | c.moveToFirst(); 224 | 225 | String suspect = c.getString(0); 226 | mCrime.setSuspect(suspect); 227 | updateCrime(); 228 | mSuspectButton.setText(suspect); 229 | } finally { 230 | c.close(); 231 | } 232 | } else if (requestCode == REQUEST_PHOTO) { 233 | updateCrime(); 234 | } 235 | } 236 | 237 | private void updateCrime() { 238 | CrimeLab.get(getActivity()).updateCrime(mCrime); 239 | mCallbacks.onCrimeUpdated(mCrime); 240 | } 241 | 242 | private void updateDate() { 243 | mDateButton.setText(mCrime.getDate().toString()); 244 | } 245 | 246 | private String getCrimeReport() { 247 | String solvedString = null; 248 | if (mCrime.isSolved()) { 249 | solvedString = getString(R.string.crime_report_solved); 250 | } else { 251 | solvedString = getString(R.string.crime_report_unsolved); 252 | } 253 | String dateFormat = "EEE, MMM dd"; 254 | String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString(); 255 | String suspect = mCrime.getSuspect(); 256 | if (suspect == null) { 257 | suspect = getString(R.string.crime_report_no_suspect); 258 | } else { 259 | suspect = getString(R.string.crime_report_suspect, suspect); 260 | } 261 | String report = getString(R.string.crime_report, 262 | mCrime.getTitle(), dateString, solvedString, suspect); 263 | return report; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.os.Environment; 8 | 9 | import com.bignerdranch.android.criminalintent.database.CrimeBaseHelper; 10 | import com.bignerdranch.android.criminalintent.database.CrimeCursorWrapper; 11 | 12 | import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable; 13 | 14 | import java.io.File; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.UUID; 18 | 19 | public class CrimeLab { 20 | private static CrimeLab sCrimeLab; 21 | 22 | private Context mContext; 23 | private SQLiteDatabase mDatabase; 24 | 25 | public static CrimeLab get(Context context) { 26 | if (sCrimeLab == null) { 27 | sCrimeLab = new CrimeLab(context); 28 | } 29 | return sCrimeLab; 30 | } 31 | 32 | private CrimeLab(Context context) { 33 | mContext = context.getApplicationContext(); 34 | mDatabase = new CrimeBaseHelper(mContext) 35 | .getWritableDatabase(); 36 | } 37 | 38 | 39 | public void addCrime(Crime c) { 40 | ContentValues values = getContentValues(c); 41 | 42 | mDatabase.insert(CrimeTable.NAME, null, values); 43 | } 44 | 45 | public List getCrimes() { 46 | List crimes = new ArrayList<>(); 47 | 48 | CrimeCursorWrapper cursor = queryCrimes(null, null); 49 | 50 | cursor.moveToFirst(); 51 | while (!cursor.isAfterLast()) { 52 | crimes.add(cursor.getCrime()); 53 | cursor.moveToNext(); 54 | } 55 | cursor.close(); 56 | 57 | return crimes; 58 | } 59 | 60 | public Crime getCrime(UUID id) { 61 | CrimeCursorWrapper cursor = queryCrimes( 62 | CrimeTable.Cols.UUID + " = ?", 63 | new String[] { id.toString() } 64 | ); 65 | 66 | try { 67 | if (cursor.getCount() == 0) { 68 | return null; 69 | } 70 | 71 | cursor.moveToFirst(); 72 | return cursor.getCrime(); 73 | } finally { 74 | cursor.close(); 75 | } 76 | } 77 | 78 | public File getPhotoFile(Crime crime) { 79 | File externalFilesDir = mContext 80 | .getExternalFilesDir(Environment.DIRECTORY_PICTURES); 81 | 82 | if (externalFilesDir == null) { 83 | return null; 84 | } 85 | 86 | return new File(externalFilesDir, crime.getPhotoFilename()); 87 | } 88 | 89 | public void updateCrime(Crime crime) { 90 | String uuidString = crime.getId().toString(); 91 | ContentValues values = getContentValues(crime); 92 | 93 | mDatabase.update(CrimeTable.NAME, values, 94 | CrimeTable.Cols.UUID + " = ?", 95 | new String[] { uuidString }); 96 | } 97 | 98 | private static ContentValues getContentValues(Crime crime) { 99 | ContentValues values = new ContentValues(); 100 | values.put(CrimeTable.Cols.UUID, crime.getId().toString()); 101 | values.put(CrimeTable.Cols.TITLE, crime.getTitle()); 102 | values.put(CrimeTable.Cols.DATE, crime.getDate().getTime()); 103 | values.put(CrimeTable.Cols.SOLVED, crime.isSolved() ? 1 : 0); 104 | values.put(CrimeTable.Cols.SUSPECT, crime.getSuspect()); 105 | 106 | return values; 107 | } 108 | 109 | private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) { 110 | Cursor cursor = mDatabase.query( 111 | CrimeTable.NAME, 112 | null, // Columns - null selects all columns 113 | whereClause, 114 | whereArgs, 115 | null, // groupBy 116 | null, // having 117 | null // orderBy 118 | ); 119 | 120 | return new CrimeCursorWrapper(cursor); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.app.Fragment; 5 | 6 | public class CrimeListActivity extends SingleFragmentActivity 7 | implements CrimeListFragment.Callbacks, CrimeFragment.Callbacks { 8 | 9 | @Override 10 | protected Fragment createFragment() { 11 | return new CrimeListFragment(); 12 | } 13 | 14 | @Override 15 | protected int getLayoutResId() { 16 | return R.layout.activity_masterdetail; 17 | } 18 | 19 | @Override 20 | public void onCrimeSelected(Crime crime) { 21 | if (findViewById(R.id.detail_fragment_container) == null) { 22 | Intent intent = CrimePagerActivity.newIntent(this, crime.getId()); 23 | startActivity(intent); 24 | } else { 25 | Fragment newDetail = CrimeFragment.newInstance(crime.getId()); 26 | 27 | getSupportFragmentManager().beginTransaction() 28 | .replace(R.id.detail_fragment_container, newDetail) 29 | .commit(); 30 | } 31 | } 32 | 33 | @Override 34 | public void onCrimeUpdated(Crime crime) { 35 | CrimeListFragment listFragment = (CrimeListFragment) 36 | getSupportFragmentManager() 37 | .findFragmentById(R.id.fragment_container); 38 | listFragment.updateUI(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.CheckBox; 15 | import android.widget.TextView; 16 | 17 | import java.util.List; 18 | 19 | public class CrimeListFragment extends Fragment { 20 | 21 | private static final String SAVED_SUBTITLE_VISIBLE = "subtitle"; 22 | 23 | private RecyclerView mCrimeRecyclerView; 24 | private CrimeAdapter mAdapter; 25 | private boolean mSubtitleVisible; 26 | private Callbacks mCallbacks; 27 | 28 | /** 29 | * Required interface for hosting activities. 30 | */ 31 | public interface Callbacks { 32 | void onCrimeSelected(Crime crime); 33 | } 34 | 35 | @Override 36 | public void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setHasOptionsMenu(true); 39 | } 40 | 41 | @Override 42 | public void onAttach(Activity activity) { 43 | super.onAttach(activity); 44 | mCallbacks = (Callbacks) activity; 45 | } 46 | 47 | @Override 48 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 49 | Bundle savedInstanceState) { 50 | View view = inflater.inflate(R.layout.fragment_crime_list, container, false); 51 | 52 | mCrimeRecyclerView = (RecyclerView) view 53 | .findViewById(R.id.crime_recycler_view); 54 | mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 55 | 56 | if (savedInstanceState != null) { 57 | mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE); 58 | } 59 | 60 | FloatingActionButton floatingActionButton = (FloatingActionButton) view.findViewById(R.id.fab); 61 | floatingActionButton.setOnClickListener(new View.OnClickListener() { 62 | @Override public void onClick(View v) { 63 | Crime crime = new Crime(); 64 | CrimeLab.get(getActivity()).addCrime(crime); 65 | updateUI(); 66 | mCallbacks.onCrimeSelected(crime); 67 | } 68 | }); 69 | 70 | updateUI(); 71 | 72 | return view; 73 | } 74 | 75 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 76 | super.onViewCreated(view, savedInstanceState); 77 | Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); 78 | AppCompatActivity activity = (AppCompatActivity) getActivity(); 79 | activity.setSupportActionBar(toolbar); 80 | } 81 | 82 | @Override 83 | public void onResume() { 84 | super.onResume(); 85 | updateUI(); 86 | } 87 | 88 | @Override 89 | public void onSaveInstanceState(Bundle outState) { 90 | super.onSaveInstanceState(outState); 91 | outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible); 92 | } 93 | 94 | @Override 95 | public void onDetach() { 96 | super.onDetach(); 97 | mCallbacks = null; 98 | } 99 | 100 | public void updateUI() { 101 | CrimeLab crimeLab = CrimeLab.get(getActivity()); 102 | List crimes = crimeLab.getCrimes(); 103 | 104 | if (mAdapter == null) { 105 | mAdapter = new CrimeAdapter(crimes); 106 | mCrimeRecyclerView.setAdapter(mAdapter); 107 | } else { 108 | mAdapter.setCrimes(crimes); 109 | mAdapter.notifyDataSetChanged(); 110 | } 111 | } 112 | 113 | private class CrimeHolder extends RecyclerView.ViewHolder 114 | implements View.OnClickListener { 115 | 116 | private TextView mTitleTextView; 117 | private TextView mDateTextView; 118 | private CheckBox mSolvedCheckBox; 119 | 120 | private Crime mCrime; 121 | 122 | public CrimeHolder(View itemView) { 123 | super(itemView); 124 | itemView.setOnClickListener(this); 125 | 126 | mTitleTextView = (TextView) itemView.findViewById(R.id.list_item_crime_title_text_view); 127 | mDateTextView = (TextView) itemView.findViewById(R.id.list_item_crime_date_text_view); 128 | mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.list_item_crime_solved_check_box); 129 | } 130 | 131 | public void bindCrime(Crime crime) { 132 | mCrime = crime; 133 | mTitleTextView.setText(mCrime.getTitle()); 134 | mDateTextView.setText(mCrime.getDate().toString()); 135 | mSolvedCheckBox.setChecked(mCrime.isSolved()); 136 | } 137 | 138 | @Override 139 | public void onClick(View v) { 140 | mCallbacks.onCrimeSelected(mCrime); 141 | } 142 | } 143 | 144 | private class CrimeAdapter extends RecyclerView.Adapter { 145 | 146 | private List mCrimes; 147 | 148 | public CrimeAdapter(List crimes) { 149 | mCrimes = crimes; 150 | } 151 | 152 | @Override 153 | public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) { 154 | LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); 155 | View view = layoutInflater.inflate(R.layout.list_item_crime, parent, false); 156 | return new CrimeHolder(view); 157 | } 158 | 159 | @Override 160 | public void onBindViewHolder(CrimeHolder holder, int position) { 161 | Crime crime = mCrimes.get(position); 162 | holder.bindCrime(crime); 163 | } 164 | 165 | @Override 166 | public int getItemCount() { 167 | return mCrimes.size(); 168 | } 169 | 170 | public void setCrimes(List crimes) { 171 | mCrimes = crimes; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.provider.MediaStore; 9 | import android.support.design.widget.CollapsingToolbarLayout; 10 | import android.support.design.widget.FloatingActionButton; 11 | import android.support.v4.app.Fragment; 12 | import android.support.v4.app.FragmentManager; 13 | import android.support.v4.app.FragmentStatePagerAdapter; 14 | import android.support.v4.view.ViewPager; 15 | import android.support.v7.app.AppCompatActivity; 16 | import android.support.v7.widget.Toolbar; 17 | import android.view.View; 18 | import android.widget.ImageView; 19 | 20 | import java.io.File; 21 | import java.util.List; 22 | import java.util.UUID; 23 | 24 | public class CrimePagerActivity extends AppCompatActivity 25 | implements CrimeFragment.Callbacks { 26 | private static final String EXTRA_CRIME_ID = 27 | "com.bignerdranch.android.criminalintent.crime_id"; 28 | private static final int REQUEST_PHOTO = 2; 29 | 30 | private ViewPager mViewPager; 31 | private List mCrimes; 32 | private ViewPager.OnPageChangeListener mOnPageChangeListener; 33 | private FloatingActionButton mPhotoButton; 34 | 35 | public static Intent newIntent(Context packageContext, UUID crimeId) { 36 | Intent intent = new Intent(packageContext, CrimePagerActivity.class); 37 | intent.putExtra(EXTRA_CRIME_ID, crimeId); 38 | return intent; 39 | } 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_crime_pager); 45 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 46 | setSupportActionBar(toolbar); 47 | 48 | UUID crimeId = (UUID) getIntent() 49 | .getSerializableExtra(EXTRA_CRIME_ID); 50 | 51 | mViewPager = (ViewPager) findViewById(R.id.activity_crime_pager_view_pager); 52 | 53 | mCrimes = CrimeLab.get(this).getCrimes(); 54 | FragmentManager fragmentManager = getSupportFragmentManager(); 55 | mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) { 56 | 57 | @Override 58 | public Fragment getItem(int position) { 59 | Crime crime = mCrimes.get(position); 60 | return CrimeFragment.newInstance(crime.getId()); 61 | } 62 | 63 | @Override 64 | public int getCount() { 65 | return mCrimes.size(); 66 | } 67 | }); 68 | 69 | mPhotoButton = (FloatingActionButton) findViewById(R.id.fab); 70 | 71 | mOnPageChangeListener = new ViewPager.OnPageChangeListener() { 72 | @Override 73 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 74 | } 75 | 76 | @Override 77 | public void onPageSelected(int position) { 78 | Crime crime = mCrimes.get(position); 79 | updateCurrentCrime(crime); 80 | final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 81 | File photoFile = CrimeLab.get(CrimePagerActivity.this).getPhotoFile(crime); 82 | 83 | boolean canTakePhoto = photoFile != null && captureImage.resolveActivity(getPackageManager()) != null; 84 | // mPhotoButton.setEnabled(canTakePhoto); 85 | 86 | if (canTakePhoto) { 87 | Uri uri = Uri.fromFile(photoFile); 88 | captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri); 89 | } 90 | 91 | mPhotoButton.setOnClickListener(new View.OnClickListener() { 92 | @Override 93 | public void onClick(View v) { 94 | startActivityForResult(captureImage, REQUEST_PHOTO); 95 | } 96 | }); 97 | 98 | } 99 | 100 | @Override 101 | public void onPageScrollStateChanged(int state) { 102 | } 103 | }; 104 | mViewPager.addOnPageChangeListener(mOnPageChangeListener); 105 | 106 | for (int i = 0; i < mCrimes.size(); i++) { 107 | if (mCrimes.get(i).getId().equals(crimeId)) { 108 | mViewPager.setCurrentItem(i); 109 | break; 110 | } 111 | } 112 | } 113 | 114 | private void updateCurrentCrime(Crime crime) { 115 | if (crime.getTitle() != null) { 116 | setTitle(crime.getTitle()); 117 | CollapsingToolbarLayout collapsingToolbar = 118 | (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); 119 | collapsingToolbar.setTitle(crime.getTitle()); 120 | } 121 | ImageView imageView = (ImageView) findViewById(R.id.backdrop); 122 | if (imageView != null) { 123 | File photoFile = CrimeLab.get(CrimePagerActivity.this).getPhotoFile(crime); 124 | if (photoFile == null || !photoFile.exists()) { 125 | imageView.setImageDrawable(null); 126 | } else { 127 | Bitmap bitmap = PictureUtils.getScaledBitmap( 128 | photoFile.getPath(), CrimePagerActivity.this); 129 | imageView.setImageBitmap(bitmap); 130 | } 131 | } 132 | } 133 | 134 | @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { 135 | // mPhotoFile = CrimeLab.get(this).getPhotoFile(); 136 | super.onActivityResult(requestCode, resultCode, data); 137 | } 138 | 139 | @Override protected void onDestroy() { 140 | super.onDestroy(); 141 | mViewPager.removeOnPageChangeListener(mOnPageChangeListener); 142 | } 143 | 144 | @Override 145 | public void onCrimeUpdated(Crime crime) { 146 | Crime currentCrime = mCrimes.get(mViewPager.getCurrentItem()); 147 | if (currentCrime == null || crime == null || !currentCrime.getId().equals(crime.getId())) { 148 | return; 149 | } 150 | 151 | updateCurrentCrime(crime); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.support.v4.app.DialogFragment; 9 | import android.support.v7.app.AlertDialog; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.widget.DatePicker; 13 | 14 | import java.util.Calendar; 15 | import java.util.Date; 16 | import java.util.GregorianCalendar; 17 | 18 | public class DatePickerFragment extends DialogFragment { 19 | 20 | public static final String EXTRA_DATE = 21 | "com.bignerdranch.android.criminalintent.date"; 22 | 23 | private static final String ARG_DATE = "date"; 24 | 25 | private DatePicker mDatePicker; 26 | 27 | public static DatePickerFragment newInstance(Date date) { 28 | Bundle args = new Bundle(); 29 | args.putSerializable(ARG_DATE, date); 30 | 31 | DatePickerFragment fragment = new DatePickerFragment(); 32 | fragment.setArguments(args); 33 | return fragment; 34 | } 35 | 36 | @Override 37 | public Dialog onCreateDialog(Bundle savedInstanceState) { 38 | Date date = (Date) getArguments().getSerializable(ARG_DATE); 39 | 40 | Calendar calendar = Calendar.getInstance(); 41 | calendar.setTime(date); 42 | int year = calendar.get(Calendar.YEAR); 43 | int month = calendar.get(Calendar.MONTH); 44 | int day = calendar.get(Calendar.DAY_OF_MONTH); 45 | 46 | View v = LayoutInflater.from(getActivity()) 47 | .inflate(R.layout.dialog_date, null); 48 | 49 | mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_date_picker); 50 | mDatePicker.init(year, month, day, null); 51 | 52 | return new AlertDialog.Builder(getActivity()) 53 | .setView(v) 54 | .setTitle(R.string.date_picker_title) 55 | .setPositiveButton(android.R.string.ok, 56 | new DialogInterface.OnClickListener() { 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | int year = mDatePicker.getYear(); 60 | int month = mDatePicker.getMonth(); 61 | int day = mDatePicker.getDayOfMonth(); 62 | Date date = new GregorianCalendar(year, month, day).getTime(); 63 | sendResult(Activity.RESULT_OK, date); 64 | } 65 | }) 66 | .create(); 67 | } 68 | 69 | private void sendResult(int resultCode, Date date) { 70 | if (getTargetFragment() == null) { 71 | return; 72 | } 73 | 74 | Intent intent = new Intent(); 75 | intent.putExtra(EXTRA_DATE, date); 76 | 77 | getTargetFragment() 78 | .onActivityResult(getTargetRequestCode(), resultCode, intent); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Point; 7 | 8 | public class PictureUtils { 9 | public static Bitmap getScaledBitmap(String path, Activity activity) { 10 | Point size = new Point(); 11 | activity.getWindowManager().getDefaultDisplay() 12 | .getSize(size); 13 | 14 | return getScaledBitmap(path, size.x, size.y); 15 | } 16 | 17 | public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) { 18 | // read in the dimensions of the image on disk 19 | BitmapFactory.Options options = new BitmapFactory.Options(); 20 | options.inJustDecodeBounds = true; 21 | BitmapFactory.decodeFile(path, options); 22 | 23 | float srcWidth = options.outWidth; 24 | float srcHeight = options.outHeight; 25 | 26 | int inSampleSize = 1; 27 | if (srcHeight > destHeight || srcWidth > destWidth) { 28 | if (srcWidth > srcHeight) { 29 | inSampleSize = Math.round(srcHeight / destHeight); 30 | } else { 31 | inSampleSize = Math.round(srcWidth / destWidth); 32 | } 33 | } 34 | 35 | options = new BitmapFactory.Options(); 36 | options.inSampleSize = inSampleSize; 37 | 38 | return BitmapFactory.decodeFile(path, options); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.LayoutRes; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | 10 | public abstract class SingleFragmentActivity extends AppCompatActivity { 11 | 12 | protected abstract Fragment createFragment(); 13 | 14 | @LayoutRes 15 | protected int getLayoutResId() { 16 | return R.layout.activity_fragment; 17 | } 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(getLayoutResId()); 23 | 24 | FragmentManager fm = getSupportFragmentManager(); 25 | Fragment fragment = fm.findFragmentById(R.id.fragment_container); 26 | 27 | if (fragment == null) { 28 | fragment = createFragment(); 29 | fm.beginTransaction() 30 | .add(R.id.fragment_container, fragment) 31 | .commit(); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent.database; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable; 8 | 9 | public class CrimeBaseHelper extends SQLiteOpenHelper { 10 | private static final String TAG = "CrimeBaseHelper"; 11 | private static final int VERSION = 2; 12 | private static final String DATABASE_NAME = "crimeBase.db"; 13 | 14 | public CrimeBaseHelper(Context context) { 15 | super(context, DATABASE_NAME, null, VERSION); 16 | } 17 | 18 | @Override 19 | public void onCreate(SQLiteDatabase db) { 20 | 21 | db.execSQL("create table " + CrimeTable.NAME + "(" + 22 | " _id integer primary key autoincrement, " + 23 | CrimeTable.Cols.UUID + ", " + 24 | CrimeTable.Cols.TITLE + ", " + 25 | CrimeTable.Cols.DATE + ", " + 26 | CrimeTable.Cols.SOLVED + ", " + 27 | CrimeTable.Cols.SUSPECT + 28 | ")" 29 | ); 30 | } 31 | 32 | @Override 33 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent.database; 2 | 3 | import android.database.Cursor; 4 | import android.database.CursorWrapper; 5 | 6 | import com.bignerdranch.android.criminalintent.Crime; 7 | 8 | import java.util.Date; 9 | import java.util.UUID; 10 | 11 | import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable; 12 | 13 | public class CrimeCursorWrapper extends CursorWrapper { 14 | public CrimeCursorWrapper(Cursor cursor) { 15 | super(cursor); 16 | } 17 | 18 | public Crime getCrime() { 19 | String uuidString = getString(getColumnIndex(CrimeTable.Cols.UUID)); 20 | String title = getString(getColumnIndex(CrimeTable.Cols.TITLE)); 21 | long date = getLong(getColumnIndex(CrimeTable.Cols.DATE)); 22 | int isSolved = getInt(getColumnIndex(CrimeTable.Cols.SOLVED)); 23 | String suspect = getString(getColumnIndex(CrimeTable.Cols.SUSPECT)); 24 | 25 | Crime crime = new Crime(UUID.fromString(uuidString)); 26 | crime.setTitle(title); 27 | crime.setDate(new Date(date)); 28 | crime.setSolved(isSolved != 0); 29 | crime.setSuspect(suspect); 30 | 31 | return crime; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java: -------------------------------------------------------------------------------- 1 | package com.bignerdranch.android.criminalintent.database; 2 | 3 | public class CrimeDbSchema { 4 | public static final class CrimeTable { 5 | public static final String NAME = "crimes"; 6 | 7 | public static final class Cols { 8 | public static final String UUID = "uuid"; 9 | public static final String TITLE = "title"; 10 | public static final String DATE = "date"; 11 | public static final String SOLVED = "solved"; 12 | public static final String SUSPECT = "suspect"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_photo_camera_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-hdpi/ic_photo_camera_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_photo_camera_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-mdpi/ic_photo_camera_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_photo_camera_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-xhdpi/ic_photo_camera_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_photo_camera_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-xxhdpi/ic_photo_camera_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_photo_camera_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/layout-land/fragment_crime.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 17 | 21 | 22 | 23 | 26 | 30 | 31 | 32 | 37 | 38 | 44 | 45 |