├── app ├── .gitignore ├── src │ └── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── arrays.xml │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ ├── drawable-xxhdpi │ │ │ └── drawable-xxhdpi │ │ │ │ └── ic_done_white_24dp.png │ │ ├── drawable-anydpi │ │ │ ├── ic_add.xml │ │ │ ├── ic_done.xml │ │ │ ├── ic_delete.xml │ │ │ ├── ic_insert_dummy.xml │ │ │ ├── ic_edit.xml │ │ │ └── ic_delete_all.xml │ │ ├── menu │ │ │ ├── menu_catalog.xml │ │ │ └── menu_editor.xml │ │ └── layout │ │ │ ├── list_item.xml │ │ │ ├── activity_movie_detail.xml │ │ │ ├── activity_catalog.xml │ │ │ └── activity_editor.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── android │ │ │ └── movieslibrary │ │ │ ├── model │ │ │ └── Movie.java │ │ │ ├── data │ │ │ ├── MoviesDbHelper.java │ │ │ ├── MoviesContract.java │ │ │ └── MoviesProvider.java │ │ │ ├── adapters │ │ │ └── MoviesCursorAdapter.java │ │ │ └── activities │ │ │ ├── CatalogActivity.java │ │ │ └── EditorActivity.java │ │ └── AndroidManifest.xml ├── documentation │ └── examples │ │ ├── list.png │ │ ├── open.png │ │ ├── save.png │ │ ├── create.png │ │ ├── delete.png │ │ ├── welcome.png │ │ └── delete-all.png ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── README.md ├── .gitignore ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/documentation/examples/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/list.png -------------------------------------------------------------------------------- /app/documentation/examples/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/open.png -------------------------------------------------------------------------------- /app/documentation/examples/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/save.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/documentation/examples/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/create.png -------------------------------------------------------------------------------- /app/documentation/examples/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/delete.png -------------------------------------------------------------------------------- /app/documentation/examples/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/welcome.png -------------------------------------------------------------------------------- /app/documentation/examples/delete-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/documentation/examples/delete-all.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 4 | 16dp 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/ic_done_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivanshsoni/MoviesLibrary2/HEAD/app/src/main/res/drawable-xxhdpi/drawable-xxhdpi/ic_done_white_24dp.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Sep 21 18:02:10 IST 2017 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_done.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #F0514B 5 | 6 | #C0403C 7 | 8 | #F0514B 9 | 10 | #2D3640 11 | 12 | #394450 13 | 14 | #000000 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_insert_dummy.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_delete_all.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_catalog.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.1" 6 | defaultConfig { 7 | applicationId "com.example.android.movieslibrary" 8 | minSdkVersion 16 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | vectorDrawables.useSupportLibrary true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile 'com.android.support:design:26.1.0' 25 | compile 'com.android.support:appcompat-v7:26.1.0' 26 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/model/Movie.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.model; 2 | 3 | /** 4 | * Created by antonioyee on 12/10/17. 5 | */ 6 | 7 | public class Movie { 8 | 9 | private String name; 10 | private Integer genre; 11 | private Integer rating; 12 | 13 | public Movie(String name, Integer genre, Integer rating) { 14 | this.name = name; 15 | this.genre = genre; 16 | this.rating = rating; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public Integer getGenre() { 28 | return genre; 29 | } 30 | 31 | public void setGenre(Integer genre) { 32 | this.genre = genre; 33 | } 34 | 35 | public Integer getRating() { 36 | return rating; 37 | } 38 | 39 | public void setRating(Integer rating) { 40 | this.rating = rating; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /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 /home/shivansh/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 24 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 27 | 28 | 29 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_movie_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 23 | 24 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @string/genre_unknown 5 | @string/genre_action 6 | @string/genre_adventure 7 | @string/genre_animation 8 | @string/genre_comedy 9 | @string/genre_crime 10 | @string/genre_drama 11 | @string/genre_fantasy 12 | @string/genre_historical 13 | @string/genre_historical_fiction 14 | @string/genre_horror 15 | @string/genre_magical_realism 16 | @string/genre_mystery 17 | @string/genre_paranoid 18 | @string/genre_philosophical 19 | @string/genre_political 20 | @string/genre_romance 21 | @string/genre_saga 22 | @string/genre_satire 23 | @string/genre_science_fiction 24 | @string/genre_slice_of_Life 25 | @string/genre_speculative 26 | @string/genre_surreal 27 | @string/genre_thriller 28 | @string/genre_urban 29 | @string/genre_western 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MoviesLibrary2 # 2 | 3 | - MoviesLibrary2 is an Android application that allows users to create a movie watch list. 4 | 5 | - Users can easily add, edit and delete movies from their watch list. 6 | 7 | #### 1) Opening the application #### 8 | Once the application is installed select the MoviesLibrary2 from your application list. 9 | 10 | 11 | 12 | 13 | #### 2) Adding a movie to your watch list #### 14 | To add a movie to your watch list click the add icon. 15 | 16 | 17 | 18 | Type in the name of the movie and select a category from the list. 19 | 20 | 21 | 22 | Select the checkmark to save the movie to your list. 23 | 24 | 25 | 26 | After you have added the movie you will be returned to the home screen(your movie list). 27 | 28 | 29 | #### 3) Editing and deleting movies #### 30 | You can view, modify or delete a movie by selecting it from the list. 31 | To edit the movie simply change the details and select the save checkmark once more. 32 | 33 | To delete a movie , select delete from the menu located in the upper right corner of the application. 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/data/MoviesDbHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.data; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | import com.example.android.movieslibrary.data.MoviesContract.MoviesEntry; 8 | 9 | public class MoviesDbHelper extends SQLiteOpenHelper { 10 | 11 | public static final String LOG_TAG = MoviesDbHelper.class.getSimpleName(); 12 | 13 | private static final String DATABASE_NAME = "shelter.db"; 14 | 15 | private static final int DATABASE_VERSION = 2; 16 | 17 | public MoviesDbHelper(Context context) { 18 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 19 | } 20 | 21 | @Override 22 | public void onCreate(SQLiteDatabase db) { 23 | String SQL_CREATE_MOVIES_TABLE = "CREATE TABLE " + MoviesEntry.TABLE_NAME + " (" 24 | + MoviesEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 25 | + MoviesEntry.COLUMN_MOVIES_NAME + " TEXT NOT NULL, " 26 | + MoviesEntry.COLUMN_MOVIES_GENRE + " INTEGER NOT NULL, " 27 | + MoviesEntry.COLUMN_MOVIES_RATING + " INTEGER NOT NULL, " 28 | + MoviesEntry.COLUMN_MOVIES_SUMMARY + " TEXT);"; 29 | 30 | db.execSQL(SQL_CREATE_MOVIES_TABLE); 31 | } 32 | 33 | @Override 34 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 35 | String upgradeQueryRating = "ALTER TABLE " + MoviesEntry.TABLE_NAME + " ADD COLUMN " + MoviesEntry.COLUMN_MOVIES_RATING + " INTEGER NOT NULL DEFAULT 0"; 36 | String upgradeQuerySummary = "ALTER TABLE " + MoviesEntry.TABLE_NAME + " ADD COLUMN " + MoviesEntry.COLUMN_MOVIES_SUMMARY+ " TEXT"; 37 | if (oldVersion == 1 && newVersion == 2) { 38 | db.execSQL(upgradeQueryRating); 39 | db.execSQL(upgradeQuerySummary); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/adapters/MoviesCursorAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.adapters; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.CursorAdapter; 9 | import android.widget.TextView; 10 | 11 | import com.example.android.movieslibrary.R; 12 | import com.example.android.movieslibrary.data.MoviesContract.MoviesEntry; 13 | 14 | public class MoviesCursorAdapter extends CursorAdapter { 15 | 16 | public static final String MOVIE_SUMMARY = "Summary"; 17 | public static final String MOVIE_NAME = "movieName"; 18 | public static final String MOVIE_GENRE = "movieGenre"; 19 | 20 | public MoviesCursorAdapter(Context context, Cursor c) { 21 | super(context, c, 0 /* flags */); 22 | } 23 | 24 | @Override 25 | public View newView(Context context, Cursor cursor, ViewGroup parent) { 26 | return LayoutInflater.from(context).inflate(R.layout.list_item, parent, false); 27 | } 28 | 29 | @Override 30 | public void bindView(final View view, final Context context, final Cursor cursor) { 31 | TextView nameTextView = view.findViewById(R.id.name); 32 | TextView genreTextView = view.findViewById(R.id.genre); 33 | 34 | int nameColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIES_NAME); 35 | int genreColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIES_GENRE); 36 | int summaryColumnIndex = cursor.getColumnIndexOrThrow(MoviesEntry.COLUMN_MOVIES_SUMMARY); 37 | 38 | final String moviesName = cursor.getString(nameColumnIndex); 39 | int genreIndex = cursor.getInt(genreColumnIndex); 40 | 41 | final String genreName = context.getResources().getStringArray(R.array.array_genre_options)[genreIndex]; 42 | nameTextView.setText(moviesName); 43 | genreTextView.setText(genreName); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 16 | 25 | 26 | 32 | 33 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_catalog.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 30 | 31 | 42 | 43 | 44 | 54 | 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | ### Android template 11 | # Built application files 12 | *.apk 13 | *.ap_ 14 | 15 | # Files for the ART/Dalvik VM 16 | *.dex 17 | 18 | # Java class files 19 | *.class 20 | 21 | # Generated files 22 | bin/ 23 | gen/ 24 | out/ 25 | 26 | # Gradle files 27 | .gradle/ 28 | build/ 29 | 30 | # Local configuration file (sdk path, etc) 31 | local.properties 32 | 33 | # Proguard folder generated by Eclipse 34 | proguard/ 35 | 36 | # Log Files 37 | *.log 38 | 39 | # Android Studio Navigation editor temp files 40 | .navigation/ 41 | 42 | # Android Studio captures folder 43 | captures/ 44 | 45 | # Intellij 46 | *.iml 47 | .idea/workspace.xml 48 | .idea/tasks.xml 49 | .idea/gradle.xml 50 | .idea/dictionaries 51 | .idea/libraries 52 | 53 | # Keystore files 54 | *.jks 55 | 56 | # External native build folder generated in Android Studio 2.2 and later 57 | .externalNativeBuild 58 | 59 | # Google Services (e.g. APIs or Firebase) 60 | google-services.json 61 | 62 | # Freeline 63 | freeline.py 64 | freeline/ 65 | freeline_project_description.json 66 | ### JetBrains template 67 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 68 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 69 | 70 | # User-specific stuff: 71 | .idea/**/workspace.xml 72 | .idea/**/tasks.xml 73 | .idea/dictionaries 74 | 75 | # Sensitive or high-churn files: 76 | .idea/**/dataSources/ 77 | .idea/**/dataSources.ids 78 | .idea/**/dataSources.xml 79 | .idea/**/dataSources.local.xml 80 | .idea/**/sqlDataSources.xml 81 | .idea/**/dynamic.xml 82 | .idea/**/uiDesigner.xml 83 | 84 | # Gradle: 85 | .idea/**/gradle.xml 86 | .idea/**/libraries 87 | 88 | # CMake 89 | cmake-build-debug/ 90 | 91 | # Mongo Explorer plugin: 92 | .idea/**/mongoSettings.xml 93 | 94 | ## File-based project format: 95 | *.iws 96 | 97 | ## Plugin-specific files: 98 | 99 | # IntelliJ 100 | out/ 101 | 102 | # mpeltonen/sbt-idea plugin 103 | .idea_modules/ 104 | 105 | # JIRA plugin 106 | atlassian-ide-plugin.xml 107 | 108 | # Cursive Clojure plugin 109 | .idea/replstate.xml 110 | 111 | # Crashlytics plugin (for Android Studio and IntelliJ) 112 | com_crashlytics_export_strings.xml 113 | crashlytics.properties 114 | crashlytics-build.properties 115 | fabric.properties 116 | 117 | /.idea/ 118 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Movies Library 4 | Insert Dummy Movie 5 | Delete All Movies 6 | Feeling Bored... 7 | Get started by adding a Movie 8 | Add a Movie 9 | Edit Movie 10 | Save 11 | Delete 12 | Movie saved 13 | Movie deletion failed 14 | Movie successfully deleted 15 | Movie updated 16 | Discard your changes and quit editing? 17 | Discard 18 | Keep Editing 19 | Movie deleted 20 | Error with deleting Movie 21 | Delete this Movie? 22 | Delete 23 | Cancel 24 | Movie Name 25 | Movie Summary 26 | Movie Type 27 | Name 28 | Summary 29 | Type 30 | Unknown 31 | Animation 32 | Action 33 | Rating 34 | Romantic 35 | Animated 36 | Science_Fiction 37 | Adventure 38 | Comedy 39 | Crime 40 | Drama 41 | Fantasy 42 | Historical 43 | Historical Fiction 44 | Horror 45 | Magical Realism 46 | Mystery 47 | Paranoid 48 | Philosophical 49 | Political 50 | Romance 51 | Saga 52 | Satire 53 | Science Fiction 54 | Slice of Life 55 | Speculative 56 | Surreal 57 | Thriller 58 | Urban 59 | Western 60 | Summary 61 | Edit 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/data/MoviesContract.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.data; 2 | 3 | import android.content.ContentResolver; 4 | import android.net.Uri; 5 | import android.provider.BaseColumns; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public final class MoviesContract { 11 | 12 | private MoviesContract() { 13 | } 14 | 15 | public static final String CONTENT_AUTHORITY = "com.example.android.movieslibrary"; 16 | 17 | public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); 18 | public static final String PATH_MOVIES = "movies"; 19 | 20 | public static final class MoviesEntry implements BaseColumns { 21 | 22 | public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_MOVIES); 23 | 24 | public static final String CONTENT_LIST_TYPE = 25 | ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIES; 26 | 27 | public static final String CONTENT_ITEM_TYPE = 28 | ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_MOVIES; 29 | 30 | 31 | public final static String TABLE_NAME = "movies"; 32 | 33 | public final static String _ID = BaseColumns._ID; 34 | 35 | public final static String COLUMN_MOVIES_NAME = "name"; 36 | 37 | public final static String COLUMN_MOVIES_GENRE = "genre"; 38 | 39 | public final static String COLUMN_MOVIES_RATING = "rating"; 40 | 41 | public final static String COLUMN_MOVIES_SUMMARY = "summary"; 42 | 43 | public static final int GENRE_UNKNOWN = 0; 44 | public static final int GENRE_ACTION = 1; 45 | public static final int GENRE_ADVENTURE = 2; 46 | public static final int GENRE_ANIMATION = 3; 47 | public static final int GENRE_COMEDY = 4; 48 | public static final int GENRE_CRIME = 5; 49 | public static final int GENRE_DRAMA = 6; 50 | public static final int GENRE_FANTASY = 7; 51 | public static final int GENRE_HISTORICAL = 8; 52 | public static final int GENRE_HISTORICAL_FICTION = 9; 53 | public static final int GENRE_HORROR = 10; 54 | public static final int GENRE_MAGICAL_REALISM = 11; 55 | public static final int GENRE_MYSTERY = 12; 56 | public static final int GENRE_PARANOID = 13; 57 | public static final int GENRE_PHILOSOPHICAL = 14; 58 | public static final int GENRE_POLITICAL = 15; 59 | public static final int GENRE_ROMANCE = 16; 60 | public static final int GENRE_SAGA = 17; 61 | public static final int GENRE_SATIRE = 18; 62 | public static final int GENRE_SCIENCE_FICTION = 19; 63 | public static final int GENRE_SLICE_OF_LIFE = 21; 64 | public static final int GENRE_SPECULATIVE = 22; 65 | public static final int GENRE_SURREAL = 23; 66 | public static final int GENRE_THRILLER = 24; 67 | public static final int GENRE_URBAN = 25; 68 | public static final int GENRE_WESTERN = 26; 69 | 70 | public static List GenreList() { 71 | return Arrays.asList( 72 | GENRE_UNKNOWN, 73 | GENRE_ACTION, 74 | GENRE_ADVENTURE, 75 | GENRE_ANIMATION, 76 | GENRE_COMEDY, 77 | GENRE_CRIME, 78 | GENRE_DRAMA, 79 | GENRE_FANTASY, 80 | GENRE_HISTORICAL, 81 | GENRE_HISTORICAL_FICTION, 82 | GENRE_HORROR, 83 | GENRE_MAGICAL_REALISM, 84 | GENRE_MYSTERY, 85 | GENRE_PARANOID, 86 | GENRE_PHILOSOPHICAL, 87 | GENRE_POLITICAL, 88 | GENRE_ROMANCE, 89 | GENRE_SAGA, 90 | GENRE_SATIRE, 91 | GENRE_SCIENCE_FICTION, 92 | GENRE_SLICE_OF_LIFE, 93 | GENRE_SPECULATIVE, 94 | GENRE_SURREAL, 95 | GENRE_THRILLER, 96 | GENRE_URBAN, 97 | GENRE_WESTERN); 98 | } 99 | 100 | public static final int RATING_UNKNOWN = 0; 101 | public static final int RATING_VERY_BAD = 1; 102 | public static final int RATING_BAD = 2; 103 | public static final int RATING_GOOD = 3; 104 | public static final int RATING_VERY_GOOD = 4; 105 | public static final int RATING_GREAT = 5; 106 | 107 | public static boolean isValidGenre(int genre) { 108 | return genre >= 0 && genre <= GenreList().size(); 109 | } 110 | 111 | public static boolean isValigRating(int rating) { 112 | return rating >= RATING_UNKNOWN && rating <= RATING_GREAT; 113 | } 114 | } 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 18 | 19 | 26 | 27 | 31 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 50 | 51 | 56 | 57 | 64 | 65 | 66 | 67 | 72 | 73 | 76 | 77 | 82 | 83 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 104 | 105 | 112 | 113 | 117 | 118 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/activities/CatalogActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.activities; 2 | 3 | import android.app.LoaderManager; 4 | import android.content.ContentUris; 5 | import android.content.ContentValues; 6 | import android.content.CursorLoader; 7 | import android.content.Intent; 8 | import android.content.Loader; 9 | import android.database.Cursor; 10 | import android.net.Uri; 11 | import android.os.Bundle; 12 | import android.support.design.widget.FloatingActionButton; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.util.Log; 15 | import android.view.Menu; 16 | import android.view.MenuItem; 17 | import android.view.View; 18 | import android.widget.AdapterView; 19 | import android.widget.ListView; 20 | 21 | import com.example.android.movieslibrary.R; 22 | import com.example.android.movieslibrary.adapters.MoviesCursorAdapter; 23 | import com.example.android.movieslibrary.data.MoviesContract.MoviesEntry; 24 | import com.example.android.movieslibrary.model.Movie; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | public class CatalogActivity extends AppCompatActivity implements 30 | LoaderManager.LoaderCallbacks { 31 | 32 | private static final int MOVIES_LOADER = 0; 33 | public static final String KEY_ADD_NEW = "add_new"; 34 | 35 | MoviesCursorAdapter mCursorAdapter; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_catalog); 41 | 42 | FloatingActionButton fab = findViewById(R.id.fab); 43 | fab.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View view) { 46 | Intent intent = new Intent(CatalogActivity.this, EditorActivity.class) 47 | .putExtra(KEY_ADD_NEW, true); 48 | startActivity(intent); 49 | } 50 | }); 51 | 52 | ListView moviesListView = findViewById(R.id.list); 53 | 54 | View emptyView = findViewById(R.id.empty_view); 55 | moviesListView.setEmptyView(emptyView); 56 | 57 | mCursorAdapter = new MoviesCursorAdapter(this, null); 58 | moviesListView.setAdapter(mCursorAdapter); 59 | 60 | moviesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 61 | @Override 62 | public void onItemClick(AdapterView adapterView, View view, int position, long id) { 63 | Intent intent = new Intent(CatalogActivity.this, EditorActivity.class); 64 | Uri currentMoviesUri = ContentUris.withAppendedId(MoviesEntry.CONTENT_URI, id); 65 | 66 | intent.setData(currentMoviesUri); 67 | 68 | startActivity(intent); 69 | } 70 | }); 71 | 72 | getLoaderManager().initLoader(MOVIES_LOADER, null, this); 73 | } 74 | 75 | public List getDummyMoviesMap() { 76 | List dummyMoviesMap = new ArrayList<>(); 77 | 78 | // Realistic ratings, arranged in increasing orderd 79 | dummyMoviesMap.add(new Movie("Star Wars - The Last Jedi", MoviesEntry.GENRE_SCIENCE_FICTION, MoviesEntry.RATING_UNKNOWN)); 80 | dummyMoviesMap.add(new Movie("The Notebook", MoviesEntry.GENRE_ROMANCE, MoviesEntry.RATING_VERY_BAD)); 81 | dummyMoviesMap.add(new Movie("Jaws", MoviesEntry.GENRE_ACTION, MoviesEntry.RATING_BAD)); 82 | dummyMoviesMap.add(new Movie("Toy Story", MoviesEntry.GENRE_ANIMATION, MoviesEntry.RATING_GOOD)); 83 | dummyMoviesMap.add(new Movie("The Matrix", MoviesEntry.GENRE_SCIENCE_FICTION, MoviesEntry.RATING_VERY_GOOD)); 84 | dummyMoviesMap.add(new Movie("Interstellar", MoviesEntry.GENRE_ACTION, MoviesEntry.RATING_GREAT)); 85 | 86 | return dummyMoviesMap; 87 | } 88 | 89 | private void insertMovies() { 90 | List dummyMoviesMap = getDummyMoviesMap(); 91 | ContentValues values = new ContentValues(); 92 | values.put(MoviesEntry.COLUMN_MOVIES_NAME, "Interstellar"); 93 | values.put(MoviesEntry.COLUMN_MOVIES_GENRE, MoviesEntry.GENRE_ACTION); 94 | values.put(MoviesEntry.COLUMN_MOVIES_SUMMARY, "This is a great movie about space travel"); 95 | 96 | for (Movie key : dummyMoviesMap) { 97 | 98 | values.put(MoviesEntry.COLUMN_MOVIES_NAME, key.getName()); 99 | values.put(MoviesEntry.COLUMN_MOVIES_GENRE, key.getGenre()); 100 | values.put(MoviesEntry.COLUMN_MOVIES_RATING, key.getRating()); 101 | 102 | getContentResolver().insert(MoviesEntry.CONTENT_URI, values); 103 | } 104 | } 105 | 106 | private void deleteAllMovies() { 107 | int rowsDeleted = getContentResolver().delete(MoviesEntry.CONTENT_URI, null, null); 108 | Log.v("CatalogActivity", rowsDeleted + " rows deleted from movies database"); 109 | } 110 | 111 | @Override 112 | public boolean onCreateOptionsMenu(Menu menu) { 113 | getMenuInflater().inflate(R.menu.menu_catalog, menu); 114 | return true; 115 | } 116 | 117 | @Override 118 | public boolean onOptionsItemSelected(MenuItem item) { 119 | switch (item.getItemId()) { 120 | case R.id.action_insert_dummy_data: 121 | insertMovies(); 122 | return true; 123 | case R.id.action_delete_all_entries: 124 | deleteAllMovies(); 125 | return true; 126 | } 127 | return super.onOptionsItemSelected(item); 128 | } 129 | 130 | @Override 131 | public Loader onCreateLoader(int i, Bundle bundle) { 132 | String[] projection = { 133 | MoviesEntry._ID, 134 | MoviesEntry.COLUMN_MOVIES_NAME, 135 | MoviesEntry.COLUMN_MOVIES_GENRE, 136 | MoviesEntry.COLUMN_MOVIES_SUMMARY}; 137 | 138 | return new CursorLoader(this, // Parent activity context 139 | MoviesEntry.CONTENT_URI, // Provider content URI to query 140 | projection, // Columns to include in the resulting Cursor 141 | null, // No selection clause 142 | null, // No selection arguments 143 | null); // Default sort order 144 | } 145 | 146 | @Override 147 | public void onLoadFinished(Loader loader, Cursor data) { 148 | mCursorAdapter.swapCursor(data); 149 | } 150 | 151 | @Override 152 | public void onLoaderReset(Loader loader) { 153 | mCursorAdapter.swapCursor(null); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/data/MoviesProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.data; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentUris; 5 | import android.content.ContentValues; 6 | import android.content.UriMatcher; 7 | import android.database.Cursor; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.net.Uri; 10 | import android.util.Log; 11 | 12 | import com.example.android.movieslibrary.data.MoviesContract.MoviesEntry; 13 | 14 | public class MoviesProvider extends ContentProvider { 15 | 16 | public static final String LOG_TAG = MoviesProvider.class.getSimpleName(); 17 | 18 | private static final int MOVIES = 100; 19 | 20 | private static final int MOVIES_ID = 101; 21 | 22 | private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 23 | 24 | static { 25 | sUriMatcher.addURI(MoviesContract.CONTENT_AUTHORITY, MoviesContract.PATH_MOVIES, MOVIES); 26 | sUriMatcher.addURI(MoviesContract.CONTENT_AUTHORITY, MoviesContract.PATH_MOVIES + "/#", MOVIES_ID); 27 | } 28 | 29 | private MoviesDbHelper mDbHelper; 30 | 31 | @Override 32 | public boolean onCreate() { 33 | mDbHelper = new MoviesDbHelper(getContext()); 34 | return true; 35 | } 36 | 37 | @Override 38 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 39 | String sortOrder) { 40 | SQLiteDatabase database = mDbHelper.getReadableDatabase(); 41 | 42 | Cursor cursor; 43 | 44 | int match = sUriMatcher.match(uri); 45 | switch (match) { 46 | case MOVIES: 47 | 48 | cursor = database.query(MoviesEntry.TABLE_NAME, projection, selection, selectionArgs, 49 | null, null, sortOrder); 50 | break; 51 | case MOVIES_ID: 52 | selection = MoviesEntry._ID + "=?"; 53 | selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) }; 54 | 55 | cursor = database.query(MoviesEntry.TABLE_NAME, projection, selection, selectionArgs, 56 | null, null, sortOrder); 57 | break; 58 | default: 59 | throw new IllegalArgumentException("Cannot query unknown URI " + uri); 60 | } 61 | 62 | cursor.setNotificationUri(getContext().getContentResolver(), uri); 63 | 64 | return cursor; 65 | } 66 | 67 | @Override 68 | public Uri insert(Uri uri, ContentValues contentValues) { 69 | final int match = sUriMatcher.match(uri); 70 | switch (match) { 71 | case MOVIES: 72 | return insertMovies(uri, contentValues); 73 | default: 74 | throw new IllegalArgumentException("Insertion is not supported for " + uri); 75 | } 76 | } 77 | 78 | private Uri insertMovies(Uri uri, ContentValues values) { 79 | String name = values.getAsString(MoviesEntry.COLUMN_MOVIES_NAME); 80 | if (name == null) { 81 | throw new IllegalArgumentException("Movies requires a name"); 82 | } 83 | 84 | Integer genre = values.getAsInteger(MoviesEntry.COLUMN_MOVIES_GENRE); 85 | if (genre == null || !MoviesEntry.isValidGenre(genre)) { 86 | throw new IllegalArgumentException("Movies requires valid genre"); 87 | } 88 | 89 | Integer rating = values.getAsInteger(MoviesEntry.COLUMN_MOVIES_RATING); 90 | if (rating == null || !MoviesEntry.isValigRating(rating)) { 91 | throw new IllegalArgumentException("Movies requires valid rating"); 92 | } 93 | 94 | String summary = values.getAsString(MoviesEntry.COLUMN_MOVIES_SUMMARY); 95 | if (summary == null) { 96 | throw new IllegalArgumentException("Movies requires a name"); 97 | } 98 | 99 | SQLiteDatabase database = mDbHelper.getWritableDatabase(); 100 | 101 | long id = database.insert(MoviesEntry.TABLE_NAME, null, values); 102 | if (id == -1) { 103 | Log.e(LOG_TAG, "Failed to insert row for " + uri); 104 | return null; 105 | } 106 | 107 | getContext().getContentResolver().notifyChange(uri, null); 108 | 109 | return ContentUris.withAppendedId(uri, id); 110 | } 111 | 112 | @Override 113 | public int update(Uri uri, ContentValues contentValues, String selection, 114 | String[] selectionArgs) { 115 | final int match = sUriMatcher.match(uri); 116 | switch (match) { 117 | case MOVIES: 118 | return updateMovies(uri, contentValues, selection, selectionArgs); 119 | case MOVIES_ID: 120 | selection = MoviesEntry._ID + "=?"; 121 | selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) }; 122 | return updateMovies(uri, contentValues, selection, selectionArgs); 123 | default: 124 | throw new IllegalArgumentException("Update is not supported for " + uri); 125 | } 126 | } 127 | 128 | 129 | private int updateMovies(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 130 | if (values.containsKey(MoviesEntry.COLUMN_MOVIES_NAME)) { 131 | String name = values.getAsString(MoviesEntry.COLUMN_MOVIES_NAME); 132 | if (name == null) { 133 | throw new IllegalArgumentException("Movies requires a name"); 134 | } 135 | } 136 | 137 | if (values.containsKey(MoviesEntry.COLUMN_MOVIES_GENRE)) { 138 | Integer genre = values.getAsInteger(MoviesEntry.COLUMN_MOVIES_GENRE); 139 | if (genre == null || !MoviesEntry.isValidGenre(genre)) { 140 | throw new IllegalArgumentException("Movies requires valid genre"); 141 | } 142 | } 143 | 144 | if (values.size() == 0) { 145 | return 0; 146 | } 147 | 148 | SQLiteDatabase database = mDbHelper.getWritableDatabase(); 149 | 150 | int rowsUpdated = database.update(MoviesEntry.TABLE_NAME, values, selection, selectionArgs); 151 | 152 | if (rowsUpdated != 0) { 153 | getContext().getContentResolver().notifyChange(uri, null); 154 | } 155 | 156 | return rowsUpdated; 157 | } 158 | 159 | @Override 160 | public int delete(Uri uri, String selection, String[] selectionArgs) { 161 | SQLiteDatabase database = mDbHelper.getWritableDatabase(); 162 | 163 | int rowsDeleted; 164 | 165 | final int match = sUriMatcher.match(uri); 166 | switch (match) { 167 | case MOVIES: 168 | rowsDeleted = database.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs); 169 | break; 170 | case MOVIES_ID: 171 | selection = MoviesEntry._ID + "=?"; 172 | selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) }; 173 | rowsDeleted = database.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs); 174 | break; 175 | default: 176 | throw new IllegalArgumentException("Deletion is not supported for " + uri); 177 | } 178 | 179 | if (rowsDeleted != 0) { 180 | getContext().getContentResolver().notifyChange(uri, null); 181 | } 182 | 183 | return rowsDeleted; 184 | } 185 | 186 | @Override 187 | public String getType(Uri uri) { 188 | final int match = sUriMatcher.match(uri); 189 | switch (match) { 190 | case MOVIES: 191 | return MoviesEntry.CONTENT_LIST_TYPE; 192 | case MOVIES_ID: 193 | return MoviesEntry.CONTENT_ITEM_TYPE; 194 | default: 195 | throw new IllegalStateException("Unknown URI " + uri + " with match " + match); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/movieslibrary/activities/EditorActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.android.movieslibrary.activities; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.LoaderManager; 5 | import android.content.ContentValues; 6 | import android.content.CursorLoader; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.content.Loader; 10 | import android.database.Cursor; 11 | import android.net.Uri; 12 | import android.os.Bundle; 13 | import android.support.design.widget.TextInputLayout; 14 | import android.support.v4.app.NavUtils; 15 | import android.support.v7.app.AppCompatActivity; 16 | import android.text.TextUtils; 17 | import android.view.Menu; 18 | import android.view.MenuItem; 19 | import android.view.MotionEvent; 20 | import android.view.View; 21 | import android.widget.AdapterView; 22 | import android.widget.ArrayAdapter; 23 | import android.widget.RatingBar; 24 | import android.widget.Spinner; 25 | import android.widget.Toast; 26 | 27 | import com.example.android.movieslibrary.R; 28 | import com.example.android.movieslibrary.data.MoviesContract.MoviesEntry; 29 | 30 | import static com.example.android.movieslibrary.activities.CatalogActivity.KEY_ADD_NEW; 31 | 32 | public class EditorActivity extends AppCompatActivity implements 33 | LoaderManager.LoaderCallbacks { 34 | 35 | private static final int EXISTING_MOVIES_LOADER = 0; 36 | 37 | private Uri mCurrentMoviesUri; 38 | 39 | private TextInputLayout mNameEditText; 40 | 41 | private RatingBar rtbRating; 42 | 43 | private TextInputLayout mMovieSummary; 44 | 45 | private Spinner mGenreSpinner; 46 | 47 | private boolean editable = false; 48 | 49 | 50 | private int mGenre = MoviesEntry.GENRE_ACTION; 51 | private int mRating = MoviesEntry.RATING_UNKNOWN; 52 | 53 | private boolean mMoviesHasChanged = false; 54 | 55 | private View.OnTouchListener mTouchListener = new View.OnTouchListener() { 56 | @Override 57 | public boolean onTouch(View view, MotionEvent motionEvent) { 58 | mMoviesHasChanged = true; 59 | return false; 60 | } 61 | }; 62 | 63 | @Override 64 | protected void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | setContentView(R.layout.activity_editor); 67 | 68 | Intent intent = getIntent(); 69 | mCurrentMoviesUri = intent.getData(); 70 | 71 | if (mCurrentMoviesUri == null) { 72 | setTitle(getString(R.string.editor_activity_title_new_movies)); 73 | 74 | invalidateOptionsMenu(); 75 | } else { 76 | 77 | 78 | getLoaderManager().initLoader(EXISTING_MOVIES_LOADER, null, this); 79 | } 80 | mNameEditText = findViewById(R.id.edit_movies_name); 81 | 82 | rtbRating = findViewById(R.id.rtbRating); 83 | 84 | mMovieSummary = findViewById(R.id.edit_movies_summary); 85 | 86 | mGenreSpinner = findViewById(R.id.spinner_genre); 87 | 88 | if (intent.getBooleanExtra(KEY_ADD_NEW, false)) { 89 | editable = true; 90 | changeEditableState(); 91 | } 92 | 93 | mNameEditText.setOnTouchListener(mTouchListener); 94 | 95 | mGenreSpinner.setOnTouchListener(mTouchListener); 96 | 97 | setupSpinner(); 98 | } 99 | 100 | private void setupSpinner() { 101 | ArrayAdapter genreSpinnerAdapter = ArrayAdapter.createFromResource(this, 102 | R.array.array_genre_options, android.R.layout.simple_spinner_item); 103 | 104 | genreSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line); 105 | 106 | mGenreSpinner.setAdapter(genreSpinnerAdapter); 107 | 108 | mGenreSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 109 | @Override 110 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 111 | 112 | String selection = (String) parent.getItemAtPosition(position); 113 | if (!TextUtils.isEmpty(selection)) { 114 | mGenre = position; 115 | } else { 116 | mGenre = MoviesEntry.GENRE_UNKNOWN; 117 | } 118 | } 119 | 120 | @Override 121 | public void onNothingSelected(AdapterView parent) { 122 | mGenre = MoviesEntry.GENRE_UNKNOWN; 123 | } 124 | }); 125 | 126 | rtbRating.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() { 127 | @Override 128 | public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) { 129 | mRating = Math.round(rating); 130 | } 131 | }); 132 | } 133 | 134 | private void saveMovies() { 135 | String nameString = mNameEditText.getEditText().getText().toString().trim(); 136 | String summaryString = mMovieSummary.getEditText().getText().toString().trim(); 137 | 138 | 139 | if (mCurrentMoviesUri == null && 140 | TextUtils.isEmpty(nameString) && TextUtils.isEmpty(summaryString) && mGenre == MoviesEntry.GENRE_UNKNOWN) { 141 | return; 142 | } 143 | 144 | ContentValues values = new ContentValues(); 145 | values.put(MoviesEntry.COLUMN_MOVIES_NAME, nameString); 146 | values.put(MoviesEntry.COLUMN_MOVIES_GENRE, mGenre); 147 | values.put(MoviesEntry.COLUMN_MOVIES_RATING, mRating); 148 | values.put(MoviesEntry.COLUMN_MOVIES_SUMMARY, summaryString); 149 | 150 | if (mCurrentMoviesUri == null) { 151 | Uri newUri = getContentResolver().insert(MoviesEntry.CONTENT_URI, values); 152 | 153 | 154 | } else { 155 | int rowsAffected = getContentResolver().update(mCurrentMoviesUri, values, null, null); 156 | 157 | 158 | } 159 | } 160 | 161 | @Override 162 | public boolean onCreateOptionsMenu(Menu menu) { 163 | getMenuInflater().inflate(R.menu.menu_editor, menu); 164 | return true; 165 | } 166 | 167 | @Override 168 | public boolean onPrepareOptionsMenu(Menu menu) { 169 | changeEditableState(); 170 | MenuItem editMenuItem = menu.findItem(R.id.action_edit); 171 | MenuItem deleteMenuItem = menu.findItem(R.id.action_delete); 172 | MenuItem saveMenuItem = menu.findItem(R.id.action_save); 173 | editMenuItem.setVisible(!editable); 174 | deleteMenuItem.setVisible(editable); 175 | saveMenuItem.setVisible(editable); 176 | if (mCurrentMoviesUri == null) { 177 | deleteMenuItem.setVisible(false); 178 | } 179 | return super.onPrepareOptionsMenu(menu); 180 | } 181 | 182 | @Override 183 | public boolean onOptionsItemSelected(MenuItem item) { 184 | switch (item.getItemId()) { 185 | case R.id.action_save: 186 | saveMovies(); 187 | finish(); 188 | return true; 189 | case R.id.action_delete: 190 | showDeleteConfirmationDialog(); 191 | return true; 192 | case R.id.action_edit: 193 | editable = !editable; 194 | changeEditableState(); 195 | return true; 196 | case android.R.id.home: 197 | if (!mMoviesHasChanged) { 198 | NavUtils.navigateUpFromSameTask(EditorActivity.this); 199 | return true; 200 | } 201 | 202 | DialogInterface.OnClickListener discardButtonClickListener = 203 | new DialogInterface.OnClickListener() { 204 | @Override 205 | public void onClick(DialogInterface dialogInterface, int i) { 206 | NavUtils.navigateUpFromSameTask(EditorActivity.this); 207 | } 208 | }; 209 | 210 | showUnsavedChangesDialog(discardButtonClickListener); 211 | return true; 212 | } 213 | return super.onOptionsItemSelected(item); 214 | } 215 | 216 | private void changeEditableState() { 217 | mNameEditText.getEditText().setEnabled(editable); 218 | mMovieSummary.getEditText().setEnabled(editable); 219 | rtbRating.setIsIndicator(!editable); 220 | mGenreSpinner.setEnabled(editable); 221 | invalidateOptionsMenu(); 222 | } 223 | 224 | @Override 225 | public void onBackPressed() { 226 | if (!mMoviesHasChanged) { 227 | super.onBackPressed(); 228 | return; 229 | } 230 | 231 | DialogInterface.OnClickListener discardButtonClickListener = 232 | new DialogInterface.OnClickListener() { 233 | @Override 234 | public void onClick(DialogInterface dialogInterface, int i) { 235 | // User clicked "Discard" button, close the current activity. 236 | finish(); 237 | } 238 | }; 239 | 240 | showUnsavedChangesDialog(discardButtonClickListener); 241 | } 242 | 243 | @Override 244 | public Loader onCreateLoader(int i, Bundle bundle) { 245 | String[] projection = { 246 | MoviesEntry._ID, 247 | MoviesEntry.COLUMN_MOVIES_NAME, 248 | MoviesEntry.COLUMN_MOVIES_GENRE, 249 | MoviesEntry.COLUMN_MOVIES_RATING, 250 | MoviesEntry.COLUMN_MOVIES_SUMMARY}; 251 | 252 | return new CursorLoader(this, 253 | mCurrentMoviesUri, 254 | projection, 255 | null, 256 | null, 257 | null); 258 | } 259 | 260 | @Override 261 | public void onLoadFinished(Loader loader, Cursor cursor) { 262 | if (cursor == null || cursor.getCount() < 1) { 263 | return; 264 | } 265 | 266 | if (cursor.moveToFirst()) { 267 | int nameColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIES_NAME); 268 | 269 | int genreColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIES_GENRE); 270 | 271 | int ratingColumIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIES_RATING); 272 | 273 | int summaryColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIES_SUMMARY); 274 | 275 | String name = cursor.getString(nameColumnIndex); 276 | 277 | int genre = cursor.getInt(genreColumnIndex); 278 | 279 | int rating = cursor.getInt(ratingColumIndex); 280 | 281 | String summary = cursor.getString(summaryColumnIndex); 282 | 283 | rtbRating.setRating(rating); 284 | mNameEditText.getEditText().setText(name); 285 | mMovieSummary.getEditText().setText(summary); 286 | 287 | mNameEditText.getEditText().setText(name); 288 | 289 | mGenreSpinner.setSelection(genre); 290 | } 291 | } 292 | 293 | @Override 294 | public void onLoaderReset(Loader loader) { 295 | mNameEditText.getEditText().setText(""); 296 | mMovieSummary.getEditText().setText(""); 297 | mGenreSpinner.setSelection(0); // Select "Unknown" genre 298 | } 299 | 300 | private void showUnsavedChangesDialog( 301 | DialogInterface.OnClickListener discardButtonClickListener) { 302 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 303 | builder.setMessage(R.string.unsaved_changes_dialog_msg); 304 | builder.setPositiveButton(R.string.discard, discardButtonClickListener); 305 | builder.setNegativeButton(R.string.keep_editing, new DialogInterface.OnClickListener() { 306 | public void onClick(DialogInterface dialog, int id) { 307 | if (dialog != null) { 308 | dialog.dismiss(); 309 | } 310 | } 311 | }); 312 | 313 | AlertDialog alertDialog = builder.create(); 314 | alertDialog.show(); 315 | } 316 | 317 | private void showDeleteConfirmationDialog() { 318 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 319 | builder.setMessage(R.string.delete_dialog_msg); 320 | builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { 321 | public void onClick(DialogInterface dialog, int id) { 322 | deleteMovies(); 323 | } 324 | }); 325 | builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 326 | public void onClick(DialogInterface dialog, int id) { 327 | if (dialog != null) { 328 | dialog.dismiss(); 329 | } 330 | } 331 | }); 332 | 333 | AlertDialog alertDialog = builder.create(); 334 | alertDialog.show(); 335 | } 336 | 337 | private void deleteMovies() { 338 | if (mCurrentMoviesUri != null) { 339 | int rowsDeleted = getContentResolver().delete(mCurrentMoviesUri, null, null); 340 | 341 | if (rowsDeleted == 0) { 342 | Toast.makeText(this, getString(R.string.editor_delete_movies_failed), 343 | Toast.LENGTH_SHORT).show(); 344 | } else { 345 | Toast.makeText(this, getString(R.string.editor_delete_movies_successful), 346 | Toast.LENGTH_SHORT).show(); 347 | } 348 | } 349 | 350 | finish(); 351 | } 352 | } --------------------------------------------------------------------------------