├── .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 |
7 |
8 |
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 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | generateDebugAndroidTestSources
19 | generateDebugSources
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
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 |
50 |
51 |
57 |
58 |
64 |
71 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_crime_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
25 |
26 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
50 |
51 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fragment.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_twopane.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_date.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_crime.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
17 |
21 |
22 |
23 |
26 |
30 |
31 |
32 |
37 |
38 |
44 |
45 |
52 |
53 |
61 |
62 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_crime_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
21 |
22 |
26 |
27 |
28 |
29 |
34 |
35 |
36 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_crime.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
22 |
23 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_camera_and_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
20 |
25 |
26 |
32 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/crime.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/refs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - @layout/activity_twopane
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFF5F5F5
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 256dp
6 | 16dp
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/refs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - @layout/activity_fragment
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CriminalIntent
4 | Hello world!
5 | Settings
6 | Enter a title for the crime.
7 | Title
8 | Details
9 | Solved
10 | Date of crime:
11 | New Crime
12 | Show Subtitle
13 | Hide Subtitle
14 | %1$s crimes
15 | Choose Suspect
16 | Send Crime Report
17 | %1$s!
18 | The crime was discovered on %2$s. %3$s, and %4$s
19 |
20 | The case is solved
21 | The case is not solved
22 | there is no suspect.
23 | the suspect is %s.
24 | CriminalIntent Crime Report
25 | Send crime report via
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/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 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bolot/DesignSupportLibDemo/84eb64f83a6b0ae928ce922df92d5ac1ecacfdf7/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Dec 09 09:26:38 EST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------