├── README.md ├── SharingApp ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── sharingapp │ │ │ └── ExampleInstrumentedTest.java │ │ ├── include │ │ └── gson-2.8.2-SNAPSHOT.jar │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── sharingapp │ │ │ │ ├── AddContactActivity.java │ │ │ │ ├── AddContactCommand.java │ │ │ │ ├── AddItemActivity.java │ │ │ │ ├── AddItemCommand.java │ │ │ │ ├── AllItemsFragment.java │ │ │ │ ├── AvailableItemsFragment.java │ │ │ │ ├── BorrowedItemsFragment.java │ │ │ │ ├── Command.java │ │ │ │ ├── Contact.java │ │ │ │ ├── ContactAdapter.java │ │ │ │ ├── ContactController.java │ │ │ │ ├── ContactList.java │ │ │ │ ├── ContactListController.java │ │ │ │ ├── ContactsActivity.java │ │ │ │ ├── DeleteContactCommand.java │ │ │ │ ├── DeleteItemCommand.java │ │ │ │ ├── Dimensions.java │ │ │ │ ├── EditContactActivity.java │ │ │ │ ├── EditContactCommand.java │ │ │ │ ├── EditItemActivity.java │ │ │ │ ├── EditItemCommand.java │ │ │ │ ├── Item.java │ │ │ │ ├── ItemAdapter.java │ │ │ │ ├── ItemController.java │ │ │ │ ├── ItemList.java │ │ │ │ ├── ItemListController.java │ │ │ │ ├── ItemsFragment.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── Observable.java │ │ │ │ ├── Observer.java │ │ │ │ └── SectionsPagerAdapter.java │ │ └── res │ │ │ ├── layout │ │ │ ├── activity_add_contact.xml │ │ │ ├── activity_add_item.xml │ │ │ ├── activity_contacts.xml │ │ │ ├── activity_edit_contact.xml │ │ │ ├── activity_edit_item.xml │ │ │ ├── activity_main.xml │ │ │ ├── all_items_fragment.xml │ │ │ ├── available_items_fragment.xml │ │ │ ├── borrowed_items_fragment.xml │ │ │ ├── contactlist_contact.xml │ │ │ └── itemlist_item.xml │ │ │ ├── menu │ │ │ └── menu_main.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── sharingapp │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── src ├── adapter_pattern ├── CoffeeMachineInterface.java ├── CoffeeTouchscreenAdapter.java └── OldCoffeeMachine.java ├── composite_pattern ├── IComponent.java ├── Playlist.java ├── Program.java └── Song.java └── observer_pattern ├── Channel.java ├── Follower.java ├── Observer.java └── Subject.java /README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns 2 | 3 | Assignments for the course of Design Patterns by University of Alberta on Coursera 4 | 5 | # Course Notes 6 | 7 | ##### Table of Contents 8 | 9 | [Week 1](#week-1) 10 | 11 | [Week 2](#week-2) 12 | 13 | [Week 3](#week-3) 14 | 15 | [Week 4](#week-4) 16 | 17 | 18 | # Week 1 19 | 20 | ## 1. Gang of Four's Pattern Catalogue 21 | 22 | Design Patterns: Elements of Reusable Object-Oriented Software – Gamma, Helm, Johnson, and Vlissides 23 | 24 | ## 2. Categories of Patterns 25 | 26 | ### 2.1 Creational Patterns 27 | 28 | Creational patterns deal with the creation or cloning new objects. 29 | 30 | #### 2.1.1 Singleton Pattern 31 | 32 | - Enforces one and only one object of a Singleton class. 33 | 34 | - Has the Singleton object globally accessible. 35 | 36 | - Example in code 37 | 38 | Not Singleton 39 | 40 | ```java 41 | public class notSingleton { 42 | //Public Constructor 43 | public notSingleton() { 44 | ... 45 | } 46 | } 47 | ``` 48 | 49 | Singleton 50 | 51 | ```java 52 | public class ExampleSingleton { // lazy construction 53 | // the class variable is null if no instance is 54 | // instantiated 55 | private static ExampleSingleton uniqueInstance = null; 56 | 57 | private ExampleSingleton() { 58 | ... 59 | } 60 | 61 | // lazy construction of the instance 62 | public static ExampleSingleton getInstance() { 63 | if (uniqueInstance == null) { 64 | uniqueInstance = new ExampleSingleton(); 65 | } 66 | Return uniqueInstance; 67 | } 68 | ... 69 | } 70 | ``` 71 | 72 | #### 2.1.2 Factory Method Pattern 73 | 74 | - Factory Objects 75 | 76 | - Example 77 | 78 | ```java 79 | public class KnifeFactory { 80 | public Knife createKnife(String knifeType) { 81 | Knife knife = null; 82 | // create Knife object 83 | If(knifeType.equals(“steak”)) { 84 | knife = new SteakKnife(); 85 | } else if (knifeType.equals(“chefs”)) { 86 | knife = new ChefsKnife(); 87 | } 88 | return knife; 89 | } 90 | } 91 | ``` 92 | 93 | ```java 94 | public class KnifeStore { 95 | private KnifeFactory factory; 96 | 97 | // require a KnifeFactory object to be passed 98 | // to this constructor: 99 | Public KnifeStore(KnifeFactory factory) { 100 | this.factory = factory; 101 | } 102 | } 103 | ``` 104 | 105 | ```java 106 | public Knife orderKnife(String knifeType) { 107 | Knife knife; 108 | //use the create method in the factory 109 | knife = factory.createKnife(knifeType); 110 | //prepare the Knife 111 | knife.sharpen(); 112 | knife.polish(); 113 | knife.package(); 114 | return knife; 115 | } 116 | ``` 117 | 118 | - Benefits of Factory Objects 119 | 120 | If there are multiple clients that want to instantiate the same set of classes, then by using a Factory object, you have cut out redundant code and made the software easier to modify. 121 | 122 | - Factory Method 123 | 124 | The Factory Method design intent is to define an interface for creating objects, but let the subclasses decide which class to instantiate. 125 | 126 | ```java 127 | public abstract class KnifeStore { 128 | public Knife orderKnife(String knifeType) { 129 | Knife knife; 130 | // now creating a knife is a method in the class 131 | knife = createKnife(knifeType); 132 | // now creating a knife is a method in the = createKnife(knifeType); 133 | knife.sharpen(); 134 | knife.polish(); 135 | knife.package(); 136 | return knife; 137 | } 138 | 139 | abstract Knife createKnife(String type); 140 | } 141 | ``` 142 | 143 | ```java 144 | public BudgetKnifeStore extends KnifeStore { 145 | // up to any subclass of KnifeStore to define this method 146 | Knife createKnife(String knifeTYpe) { 147 | if(knifeType.equals("steak")) { 148 | return new BudgetSteakKnife(); 149 | }else if(knifeType.equals("chefs")) { 150 | return new BudgetChefsKnife(); 151 | } 152 | //.. more types 153 | else { 154 | return null; 155 | } 156 | } 157 | } 158 | ``` 159 | 160 | - UML Diagrams 161 | 162 | ![](https://i.imgur.com/LyY6zvN.png) 163 | 164 | ![](https://i.imgur.com/zFQ20Ce.png) 165 | 166 | ### 2.2 Structural Patterns 167 | 168 | Structural patterns describe how objects are connected to each other. 169 | 170 | #### 2.2.1 Façade Pattern 171 | 172 | - What are the conditions required for you to use the facade design pattern? 173 | 174 | - You need to simplify the interaction with your subsystem for client classes. 175 | - You need a class to act as an interface between your subsystem and a client class. 176 | 177 | - Façade 178 | 179 | A façade is a wrapper class that encapsulates a subsystem in order to hide 180 | the subsystem’s complexity, and acts as a point of entry into a subsystem 181 | without adding more functionality in itself. 182 | 183 | - Without the pattern 184 | 185 | ![](https://i.imgur.com/C3Fp1ke.png) 186 | 187 | - With the pattern 188 | 189 | ![](https://i.imgur.com/zqHStvH.png) 190 | 191 | - Step 1: Design the Interface 192 | 193 | ```java 194 | public interface IAccount { 195 | public void deposit(BigDecimal amount); 196 | public void withdraw(BigDecimal amount); 197 | public void transfer(BigDecimal amount); 198 | public int getAccountNumber(); 199 | } 200 | ``` 201 | 202 | - Step 2: Implement the Interface with one or more classes 203 | 204 | ```java 205 | public class Chequing implements IAccount {... } 206 | public class Saving implements IAccount { ... } 207 | public class Investment implements IAccount { ... } 208 | ``` 209 | 210 | - Step 3: Create the façade class and wrap the classes that 211 | implement the interface 212 | 213 | ```java 214 | public class BankService { 215 | private Hashtable bankAccounts; 216 | 217 | public BankService() { 218 | this.bankAccounts = new Hashtable 219 | } 220 | 221 | public int createNewAccount(String type, BigDecimal initAmount) { 222 | IAccount newAccount = null; 223 | switch (type) { 224 | case "chequing": 225 | newAccount = new Chequing(initAmount); 226 | break; 227 | case "saving": 228 | newAccount = new Saving(initAmount); 229 | break; 230 | case "investment": 231 | newAccount = new Investment(initAmount); 232 | break; 233 | default: 234 | System.out.println("Invalid account type"); 235 | break; 236 | } 237 | if (newAccount != null) { 238 | this.bankAccounts.put(newAccount.getAc countNumber(), newAccount); 239 | return newAccount.getAccountNumber(); 240 | } 241 | return -1; 242 | } 243 | 244 | public void transferMoney(int to, int from, BigDecimal amount) { 245 | IAccount toAccount = this.bankAccounts.get(to); 246 | IAccount fromAccount = this.bankAccounts.get(from); 247 | fromAccount.transfer(toAccount, amount); 248 | } 249 | } 250 | ``` 251 | 252 | - Step 4: Use the façade class to access the subsystem 253 | 254 | ```java 255 | public class Customer { 256 | public static void main(String args[]) { 257 | BankService myBankService = new BankService(); 258 | int mySaving = myBankService.createNewAccount("saving", new BigDecimal(500.00)); 259 | int myInvestment = myBankService.createNewAccount("investment", new BigDecimal(1000.00)); 260 | 261 | myBankService.transferMoney(mySaving, myInvestment, new BigDecimal(300.00)); 262 | } 263 | } 264 | ``` 265 | 266 | - What are the key design principles are used to implement the facade design pattern? 267 | 268 | Encapsulation, information hiding, separation of concerns 269 | 270 | - In summary, the façade design pattern: 271 | 272 | - Is a means to hide the complexity of a subsystem by encapsulating 273 | it behind a unifying wrapper called a façade class. 274 | - Removes the need for client classes to manage a subsystem on 275 | their own, resulting in less coupling between the subsystem and the 276 | client classes. 277 | - Handles instantiation and redirection of tasks to the appropriate 278 | class within the subsystem. 279 | - Provides client classes with a simplified interface for the subsystem. 280 | - Acts simply as a point of entry to a subsystem and does not add 281 | more functional the subsystem. 282 | 283 | #### 2.2.2 Adapter Pattern 284 | 285 | ![](https://i.imgur.com/hAbK8ao.png) 286 | 287 | - Example 288 | 289 | ![](https://i.imgur.com/3L3WKtV.png) 290 | 291 | - Step 1: Design the target interface 292 | 293 | ```java 294 | public interface WebRequester { 295 | public int request(Object); 296 | } 297 | ``` 298 | 299 | - Step 2: Implement the target interface with the adapter 300 | class 301 | 302 | ```java 303 | public class WebAdapter implements WebRequester { 304 | private WebService service; 305 | 306 | public void connect(WebService currentService) { 307 | this.service = currentService; 308 | /* Connect to the web service */ 309 | } 310 | 311 | public int request(Object request) { 312 | Json result = this.toJson(request); 313 | Json response = service.request(result); 314 | if (response != null) 315 | return 200; // OK status code 316 | return 500; // Server error status code 317 | } 318 | 319 | private Json toJson(Object input) { ... } 320 | } 321 | ``` 322 | 323 | - Step 3: Send the request from the client to the adapter 324 | using the target interface 325 | 326 | ```java 327 | public class WebClient { 328 | private WebRequester webRequester; 329 | 330 | public WebClient(WebRequester webRequester) { 331 | this.webRequester = webRequester; 332 | } 333 | 334 | private Object makeObject() { ... } // Make an Object 335 | 336 | public void doWork() { 337 | Object object = makeObject(); 338 | int status = webRequester.request(object); 339 | if (status == 200) { 340 | System.out.println("OK"); 341 | } else { 342 | System.out.println("Not OK"); 343 | } 344 | return; 345 | } 346 | } 347 | ``` 348 | 349 | ```java 350 | public class Program { 351 | public static void main(String args[]) { 352 | String webHost = "Host: https://google.com\n\r"; 353 | WebService service = new WebService(webHost); 354 | WebAdapter adapter = new WebAdapter(); 355 | adapter.connect(service); 356 | WebClient client = new WebClient(adapter); 357 | client.doWork(); 358 | } 359 | } 360 | ``` 361 | 362 | - What are the characteristics of the adapter design pattern? 363 | 364 | - The client and adaptee classes have incompatible interfaces. 365 | - An adapter is a wrapper class that wraps the adaptee, hiding it from the client. 366 | - The client sends requests indirectly to the adaptee by using the adapter’s target interface. 367 | - The adapter translates the request sent by the client class into a request that the adaptee class is expecting. 368 | 369 | - In summary, an adapter is meant to: 370 | 371 | - Wrap the adaptee and exposes a target interface to the client. 372 | - Indirectly change the adaptee’s interface into one that the client is 373 | expecting by implementing a target interface. 374 | - Indirectly translate the client’s request into one that the adaptee is 375 | expecting. 376 | - Reuse an existing adaptee with an incompatible interface. 377 | 378 | #### Assignment: Adapter Pattern 379 | 380 | Use this UML class diagram to help modify the code. 381 | 382 | ![](https://i.imgur.com/Y9MhsXK.png) 383 | 384 | * Code 385 | 386 | CoffeeMachineInterface.java 387 | 388 | ``````java 389 | public interface CoffeeMachineInterface { 390 | public void chooseFirstSelection(); 391 | public void chooseSecondSelection(); 392 | } 393 | `````` 394 | 395 | OldCoffeeMachine.java 396 | 397 | ```java 398 | public class OldCoffeeMachine { 399 | 400 | public void selectA() { 401 | // Select A 402 | System.out.println(“A - Selected”); 403 | } 404 | 405 | Public void selectB() { 406 | // Select B 407 | System.out.println(“B - Selected”); 408 | } 409 | } 410 | ``` 411 | 412 | CoffeeTouchscreenAdapter.java 413 | 414 | ```java 415 | public class CoffeeTouchscreenAdapter implements CoffeeMachineInterface { 416 | 417 | private OldCoffeeMachine oldMachine; 418 | 419 | public CoffeeTouchscreenAdapter(OldCoffeeMachine newMachine) { 420 | oldMachine = newMachine; 421 | } 422 | 423 | @Override 424 | public void chooseFirstSelection() { 425 | oldMachine.selectA(); 426 | } 427 | 428 | @Override 429 | public void chooseSecondSelection() { 430 | oldMachine.selectB(); 431 | } 432 | } 433 | ``` 434 | 435 | #### 2.2.3 Composite Pattern 436 | 437 | - A composite design pattern is meant to achieve two goals: 438 | 439 | - To compose nested structures of objects, and 440 | - To deal with the classes for theses objects uniformly. 441 | 442 | - The following basic design 443 | 444 | ![](https://i.imgur.com/U9jVhSI.png) 445 | 446 | - Think of composite design patterns as trees: 447 | 448 | ![](https://i.imgur.com/D9w0P36.png) 449 | 450 | - The composite design pattern is used to address two issues. 451 | 452 | - How do we use individual types of objects to build a tree-like structure? 453 | - How can we treat the individual types of objects uniformly without checking their types? 454 | 455 | - How are these two issues addressed by the composite design pattern? 456 | 457 | - Each individual class is a subtype of an interface or superclass, and will be able to conform to a set of shared behaviors. 458 | - The composite class is capable of aggregating component classes, which will create a tree-like structure. 459 | 460 | - Example 461 | 462 | ![](https://i.imgur.com/dgHYAb1.png) 463 | 464 | - Step 1: Design the interface that defines the overall type 465 | 466 | This supports polymorphism for your component and leaf 467 | classes. 468 | 469 | ```java 470 | public interface IStructure { 471 | public void enter(); 472 | public void exit(); 473 | public void location(); 474 | } 475 | ``` 476 | 477 | - Step 2: Implement the composite class 478 | 479 | ![](https://i.imgur.com/Ix1cERh.png) 480 | 481 | - Step 3: Implement the leaf class 482 | 483 | ![](https://i.imgur.com/dFQbdvE.png) 484 | 485 | - How to use it 486 | 487 | ![](https://i.imgur.com/iwgj2NP.png) 488 | 489 | - In summary, a composite design pattern allows you to build a tree-like structure of objects and treat individual types of those objects uniformly. This is achieved by: 490 | 491 | - Enforcing polymorphism across each class through implementing an interface (or inheriting from a superclass). 492 | - Using a technique called recursive composition which allows objects to be composed of other objects that are of a common type. 493 | 494 | #### Practice Peer-graded Assignment: Composite Pattern 495 | 496 | Use the UML class diagram pictured below to help modify the provided code. 497 | 498 | ![](https://i.imgur.com/jRtjTR2.png) 499 | 500 | ``` 501 | -------------------- 502 | [Program.java] 503 | -------------------- 504 | public class Program { 505 | 506 | public static void main(String args[]) { 507 | 508 | // Make new empty "Study" playlist 509 | Playlist studyPlaylist = new Playlist("Study"); 510 | 511 | // Make "Synth Pop" playlist and add 2 songs to it. 512 | Playlist synthPopPlaylist = new Playlist("Synth Pop"); 513 | Song synthPopSong1 = new Song("Girl Like You", "Toro Y Moi" ); 514 | Song synthPopSong2 = new Song("Outside", "TOPS"); 515 | synthPopPlaylist.add(synthPopSong1); 516 | synthPopPlaylist.add(synthPopSong2); 517 | 518 | // Make "Experimental" playlist and add 3 songs to it, 519 | // then set playback speed of the playlist to 0.5x 520 | Playlist experimentalPlaylist = new Playlist("Experimental"); 521 | Song experimentalSong1 = new Song("About you", "XXYYXX"); 522 | Song experimentalSong2 = new Song("Motivation", "Clams Casino"); 523 | Song experimentalSong3 = new Song("Computer Vision", "Oneohtrix Point Never"); 524 | experimentalPlaylist.add(experimentalSong1); 525 | experimentalPlaylist.add(experimentalSong2); 526 | experimentalPlaylist.add(experimentalSong3); 527 | float slowSpeed = 0.5f; 528 | experimentalPlaylist.setPlaybackSpeed(slowSpeed); 529 | 530 | // Add the "Synth Pop" playlist to the "Experimental" playlist 531 | experimentalPlaylist.add(synthPopPlaylist); 532 | 533 | // Add the "Experimental" playlist to the "Study" playlist 534 | studyPlaylist.add(experimentalPlaylist); 535 | 536 | // Create a new song and set its playback speed to 1.25x, play this song, 537 | // get the name of glitchSong → "Textuell", then get the artist of this song → "Oval" 538 | Song glitchSong = new Song("Textuell", "Oval"); 539 | float fasterSpeed = 1.25f; 540 | glitchSong.setPlaybackSpeed(fasterSpeed); 541 | glitchSong.play(); 542 | String name = glitchSong.getName(); 543 | String artist = glitchSong.getArtist(); 544 | System.out.println ("The song name is " + name ); 545 | System.out.println ("The song artist is " + artist ); 546 | 547 | // Add glitchSong to the "Study" playlist 548 | studyPlaylist.add(glitchSong); 549 | 550 | // Play "Study" playlist. 551 | studyPlaylist.play(); 552 | 553 | // Get the playlist name of studyPlaylist → "Study" 554 | System.out.println ("The Playlist's name is " + studyPlaylist.getName() ); 555 | } 556 | } 557 | 558 | -------------------- 559 | [IComponent.java] 560 | -------------------- 561 | public interface IComponent { 562 | 563 | // Your code goes here! 564 | 565 | } 566 | 567 | -------------------- 568 | [Playlist.java] 569 | -------------------- 570 | public class Playlist implements IComponent { 571 | 572 | public String playlistName; 573 | public ArrayList playlist = new ArrayList(); 574 | 575 | public Playlist(String playlistName) { 576 | this.playlistName = playlistName; 577 | } 578 | 579 | // Your code goes here! 580 | 581 | } 582 | 583 | -------------------- 584 | [Song.java] 585 | -------------------- 586 | public class Song implements IComponent { 587 | public String songName; 588 | public String artist; 589 | public float speed = 1; // Default playback speed 590 | 591 | public Song(String songName, String artist ) { 592 | this.songName = songName; 593 | this.artist = artist; 594 | } 595 | 596 | // Your code goes here! 597 | 598 | } 599 | ``` 600 | 601 | * Solution 602 | 603 | ``` 604 | -------------------- 605 | [Program.java] 606 | -------------------- 607 | public class Program { 608 | 609 | public static void main(String args[]) { 610 | 611 | // Make new empty "Study" playlist 612 | Playlist studyPlaylist = new Playlist("Study"); 613 | 614 | // Make "Synth Pop" playlist and add 2 songs to it. 615 | Playlist synthPopPlaylist = new Playlist("Synth Pop"); 616 | Song synthPopSong1 = new Song("Girl Like You", "Toro Y Moi" ); 617 | Song synthPopSong2 = new Song("Outside", "TOPS"); 618 | synthPopPlaylist.add(synthPopSong1); 619 | synthPopPlaylist.add(synthPopSong2); 620 | 621 | // Make "Experimental" playlist and add 3 songs to it, 622 | // then set playback speed of the playlist to 0.5x 623 | Playlist experimentalPlaylist = new Playlist("Experimental"); 624 | Song experimentalSong1 = new Song("About you", "XXYYXX"); 625 | Song experimentalSong2 = new Song("Motivation", "Clams Casino"); 626 | Song experimentalSong3 = new Song("Computer Vision", "Oneohtrix Point Never"); 627 | experimentalPlaylist.add(experimentalSong1); 628 | experimentalPlaylist.add(experimentalSong2); 629 | experimentalPlaylist.add(experimentalSong3); 630 | float slowSpeed = 0.5f; 631 | experimentalPlaylist.setPlaybackSpeed(slowSpeed); 632 | 633 | // Add the "Synth Pop" playlist to the "Experimental" playlist 634 | experimentalPlaylist.add(synthPopPlaylist); 635 | 636 | // Add the "Experimental" playlist to the "Study" playlist 637 | studyPlaylist.add(experimentalPlaylist); 638 | 639 | // Create a new song and set its playback speed to 1.25x, play this song, 640 | // get the name of glitchSong → "Textuell", then get the artist of this song → "Oval" 641 | Song glitchSong = new Song("Textuell", "Oval"); 642 | float fasterSpeed = 1.25f; 643 | glitchSong.setPlaybackSpeed(fasterSpeed); 644 | glitchSong.play(); 645 | String name = glitchSong.getName(); 646 | String artist = glitchSong.getArtist(); 647 | System.out.println ("The song name is " + name ); 648 | System.out.println ("The song artist is " + artist ); 649 | 650 | // Add glitchSong to the "Study" playlist 651 | studyPlaylist.add(glitchSong); 652 | 653 | // Play "Study" playlist. 654 | studyPlaylist.play(); 655 | 656 | // Get the playlist name of studyPlaylist → "Study" 657 | System.out.println ("The Playlist's name is " + studyPlaylist.getName() ); 658 | } 659 | } 660 | 661 | -------------------- 662 | [IComponent.java] 663 | -------------------- 664 | public interface IComponent { 665 | void play(); 666 | void setPlaybackSpeed(float speed); 667 | String getName(); 668 | } 669 | 670 | -------------------- 671 | [Playlist.java] 672 | -------------------- 673 | public class Playlist implements IComponent { 674 | 675 | public String playlistName; 676 | public ArrayList playlist = new ArrayList(); 677 | 678 | public Playlist(String playlistName) { 679 | this.playlistName = playlistName; 680 | } 681 | 682 | public void add(IComponent component) { 683 | playlist.add(component); 684 | } 685 | 686 | public void remove(IComponent component) { 687 | playlist.remove(component); 688 | } 689 | 690 | public void play(){ 691 | for(IComponent component : playlist) { 692 | component.play(); 693 | } 694 | } 695 | 696 | public void setPlaybackSpeed(float speed) { 697 | for(IComponent component : this.playlist){ 698 | component.setPlaybackSpeed(speed); 699 | } 700 | } 701 | 702 | public String getName() { 703 | return this.playlistName; 704 | } 705 | } 706 | 707 | -------------------- 708 | [Song.java] 709 | -------------------- 710 | public class Song implements IComponent { 711 | public String songName; 712 | public String artist; 713 | public float speed = 1; // Default playback speed 714 | 715 | public Song(String songName, String artist ) { 716 | this.songName = songName; 717 | this.artist = artist; 718 | } 719 | 720 | public void play() { 721 | // Play the song using this.speed 722 | } 723 | 724 | public void setPlaybackSpeed(float speed) { 725 | this.speed = speed; 726 | } 727 | 728 | public String getName() { 729 | return this.songName; 730 | } 731 | 732 | public String getArtist() { 733 | return this.artist; 734 | } 735 | } 736 | ``` 737 | 738 | #### 2.2.4 Proxy Pattern 739 | 740 | - The three most common scenarios where proxy classes are used are: 741 | 742 | - To act as a virtual proxy. 743 | - To act as a protection proxy. 744 | - To act as a remote proxy. 745 | 746 | - The UML diagram 747 | 748 | ![](https://i.imgur.com/ilzXxLY.png) 749 | 750 | - Proxy and RealSubject are subtypes of Subject. 751 | - The proxy design pattern achieves polymorphism through implementing a Subject interface. 752 | 753 | - Example 754 | 755 | ![](https://i.imgur.com/pQ0LteA.png) 756 | 757 | - Step 1: Design the subject interface 758 | 759 | ![](https://i.imgur.com/0uHuTei.png) 760 | 761 | 762 | - Step 2: Implement the real subject class 763 | 764 | ![](https://i.imgur.com/CfM1mzo.png) 765 | 766 | - Step 3: Implement the proxy class 767 | 768 | ![](https://i.imgur.com/GwoaYKo.png) 769 | 770 | - Which are key responsibilities of the proxy class? 771 | 772 | - It protects the real subject class by checking the client's request and controlling access to the real subject class. 773 | - It acts as a wrapper class for the real subject class. 774 | 775 | - The main features of a proxy design pattern are: 776 | 777 | - To use the proxy class to wrap the real subject class. 778 | - To have a polymorphic design so that the client class can expect the same interface for the proxy and real subject classes. 779 | - To use a lightweight proxy in place of a resource intensive object until it is actually needed. 780 | - To implement some form of intelligent verification of requests from client code in order to determine if, how, and to whom the requests should be forwarded to. 781 | - To present a local representation of a system that is not in the same physical or virtual space. 782 | 783 | #### 2.2.5 Decorator Pattern 784 | 785 | - Decorator aggregates other types of components which will allow us to stack components on top of each other, and decorator serves as the abstract superclass of concrete decorator classes that will provide an increment of behavior. 786 | 787 | ![](https://i.imgur.com/8dFYQ4R.png) 788 | 789 | - What are the reasons for using the decorator design pattern? 790 | 791 | - To reduce the number of classes needed to offer a combination of behaviors. 792 | - The decorator design pattern allows objects to dynamically add behaviors to others. 793 | 794 | - Example 795 | 796 | ![](https://i.imgur.com/ETQokYe.png) 797 | 798 | - Step 1: Design the component interface 799 | 800 | ![](https://i.imgur.com/5WoOjF8.png) 801 | 802 | 803 | - Step 2: Implement the interface with your base concrete component class 804 | 805 | ![](https://i.imgur.com/FxJ7oCL.png) 806 | 807 | - Step 3: Implement the interface with your abstract decorator class 808 | 809 | ![](https://i.imgur.com/zQHaftg.png) 810 | 811 | - Step 4: Inherit from the abstract decorator and implement the component interface with concrete decorator classes 812 | 813 | ![](https://i.imgur.com/jqweOeP.png) 814 | 815 | 816 | - In action 817 | 818 | ![](https://i.imgur.com/pBpdXc9.png) 819 | 820 | 821 | ![](https://i.imgur.com/K8m75rR.png) 822 | 823 | - The main features of a decorate design pattern are: 824 | 825 | - We can add, in effect, any number of behaviors dynamically to an object at runtime by using aggregation as a substitute for pure inheritance 826 | - Polymorphism is achieved by implementing a single interface. 827 | - Aggregation lets us create a stack of objects. 828 | - Each decorator object in the stack is aggregated in a one-to-one relationship with the object below it in the stack. And, 829 | - By combining aggregation and polymorphism, we can recursively invoke the same behavior down the stack and have the behavior execute upwards from the concrete component object. 830 | 831 | ### 2.3 Behavioural patterns 832 | 833 | Behavioural patterns focus on how objects distribute work, and describe 834 | how each object does a single cohesive function. Behavioural patterns also 835 | focus on how independent objects work towards a common goal. 836 | 837 | 838 | # Week 2 839 | 840 | ## 1. Behavioural patterns 841 | 842 | - These are patterns that focus on ways that individual objects collaborate to achieve a common goal. 843 | 844 | ### 1.1 Template Method Pattern 845 | 846 | - The template method defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. 847 | 848 | - The UML for a self-driving vehicle based template method. 849 | 850 | ![](https://i.imgur.com/QPNFq3z.png) 851 | 852 | ![](https://i.imgur.com/uGlYx0m.jpg) 853 | 854 | ![](https://i.imgur.com/Q13a4Fh.jpg) 855 | 856 | - Example 857 | 858 | ![](https://i.imgur.com/xkUT2BY.png) 859 | 860 | ![](https://i.imgur.com/5Xdza16.png) 861 | 862 | ![](https://i.imgur.com/OgMFf2B.png) 863 | 864 | ### 1.2 Chain of Responsibility Pattern 865 | 866 | - The chain of responsibility design pattern is a chain of objects that are responsible for handling requests. 867 | 868 | ![](https://i.imgur.com/quDBmsH.png) 869 | 870 | 871 | - This pattern is similar to exception handling in Java. 872 | 873 | ![](https://i.imgur.com/02AsKIj.png) 874 | 875 | 876 | - The UML diagram 877 | 878 | ![](https://i.imgur.com/O8RmEgX.png) 879 | 880 | - Each filter needs to go through the following steps: 881 | 882 | - 1. Check if rule matches 883 | 884 | - 2. If it matches, do something specific 885 | 886 | - 3. If it doesn’t match, call the next filter in the list 887 | 888 | It may be helpful to use the template pattern learned in a previous lesson to ensure that each class will handle a request in a similar way, following the above steps. 889 | 890 | ### 1.3 State Pattern 891 | 892 | - This pattern is primarily used when you need to change the behaviour of an object based upon changes to its internal state or the state it is in at run-time. 893 | 894 | - The UML diagram 895 | 896 | ![](https://i.imgur.com/C4A45xN.png) 897 | 898 | - For example, imagine you want to purchase a chocolate bar from the vending machine, that costs one dollar. Below is the UML state diagram: 899 | 900 | ![](https://i.imgur.com/nAbFXY6.png) 901 | 902 | 903 | - The State interface 904 | 905 | - UML diagram 906 | 907 | ![](https://i.imgur.com/SdDOrkS.png) 908 | 909 | - Code 910 | 911 | ![](https://i.imgur.com/igS0v1q.png) 912 | 913 | - States 914 | 915 | - Idle 916 | 917 | ![](https://i.imgur.com/qPSJSPs.png) 918 | 919 | - HasOneDollar 920 | 921 | ![](https://i.imgur.com/wueT2Up.png) 922 | 923 | ![](https://i.imgur.com/j5OXM8y.png) 924 | 925 | - Vending Machine 926 | 927 | ![](https://i.imgur.com/HFkD3gB.png) 928 | 929 | ![](https://i.imgur.com/UaiAwTA.png) 930 | 931 | ### 1.4 Command Pattern 932 | 933 | - Purposes of the Command Pattern: 934 | - One purpose of using the command pattern is to store and schedule different requests. One is to store and schedule different requests. If requests are turned into 935 | command objects in your software, then 936 | - Store them into lists 937 | - Manipulate them before they are completed 938 | - Put them onto a queue 939 | - Another purpose for the command pattern is to allow command to be 940 | undone or redone. 941 | - The UML diagram 942 | ![](https://i.imgur.com/FclD4Ny.png) 943 | - Example of "pasting text" 944 | - Concerte command: 945 | ![](https://i.imgur.com/iiyhhiR.png) 946 | - Invoker: 947 | ![](https://i.imgur.com/YPmh5az.png) 948 | - Benefits of the Command Pattern: 949 | - Allows commands to be manipulated as objects 950 | - Decouples the objects 951 | - Allows logic to be pulled from user interfaces 952 | 953 | ### 1.5 Mediator Pattern 954 | 955 | In the Mediator pattern, you will add an object that will talk to all of these other objects and coordinate their activities 956 | 957 | - Implementation 958 | ![](https://i.imgur.com/mwOXl7m.png) 959 | - A common usage for the mediator pattern, identified by the Gang of Four, is for dialog boxes with many components such as a check box or a radio button 960 | 961 | ### 1.6 Observer Pattern 962 | 963 | A pattern where a subject keeps a list of observers. 964 | 965 | - Imagine you have subscribed to a blog, and would like to receive notifications of any changes made to the blog. 966 | - The sequence diagram for this example: 967 | ![](https://i.imgur.com/xRTpgWM.png) 968 | - The UML diagram for this example: 969 | ![](https://i.imgur.com/4umqkUq.png) 970 | - The Java code for this example: 971 | ![](https://i.imgur.com/nGxSUgj.png) 972 | ![](https://i.imgur.com/gty1xZ9.png) 973 | ![](https://i.imgur.com/Y8EXrZA.png) 974 | ![](https://i.imgur.com/2Hkkjp6.png) 975 | 976 | #### Assignment: Observer Pattern 977 | 978 | Use the UML class diagram below to help modify the provided code 979 | ![](https://i.imgur.com/p2OIjWm.png) 980 | 981 | * Code 982 | 983 | ``` 984 | -------------------- 985 | [Subject.java] 986 | -------------------- 987 | public interface Subject { 988 | 989 | } 990 | 991 | -------------------- 992 | Channel.java 993 | -------------------- 994 | public class Channel implements Subject { 995 | 996 | } 997 | 998 | -------------------- 999 | Observer.java 1000 | -------------------- 1001 | public interface Observer { 1002 | public void update(String status); 1003 | } 1004 | 1005 | -------------------- 1006 | Follower.java 1007 | -------------------- 1008 | public class Follower implements Observer { 1009 | 1010 | } 1011 | ``` 1012 | 1013 | * Solution 1014 | 1015 | ``` 1016 | -------------------- 1017 | [Subject.java] 1018 | -------------------- 1019 | public interface Subject { 1020 | public void registerObserver(Observer observer); 1021 | public void removeObserver(Observer observer); 1022 | public void notifyObservers(); 1023 | } 1024 | 1025 | -------------------- 1026 | Channel.java 1027 | -------------------- 1028 | private ArrayList observers = new ArrayList(); 1029 | private String channelName; 1030 | private String status; 1031 | 1032 | public Channel(String channelName, String status) { 1033 | this.channelName = channelName; 1034 | this.status = status; 1035 | } 1036 | 1037 | public String getChannelName() { 1038 | return channelName; 1039 | } 1040 | 1041 | public void setChannelName(String channelName) { 1042 | this.channelName = channelName; 1043 | } 1044 | 1045 | public string getStatus() { 1046 | return status; 1047 | } 1048 | 1049 | public void setStatus(String status) { 1050 | this.status = status; 1051 | notifyObservers(); 1052 | } 1053 | 1054 | public void notifyObservers() { 1055 | for (Observer obs : observers) { 1056 | obs.update(this.status); 1057 | } 1058 | } 1059 | 1060 | public void registerObserver(Observer observer) { 1061 | observers.add(observer); 1062 | } 1063 | 1064 | public void removeObserver(Observer observer) { 1065 | observers.remove(observer); 1066 | } 1067 | 1068 | -------------------- 1069 | Observer.java 1070 | -------------------- 1071 | public interface Observer { 1072 | public void update(String status); 1073 | } 1074 | 1075 | -------------------- 1076 | Follower.java 1077 | -------------------- 1078 | public class Follower implements Observer { 1079 | 1080 | private String followerName; 1081 | 1082 | public Follower(String followerName) { 1083 | this.followerName = followerName; 1084 | } 1085 | 1086 | public String getFollowerName() { 1087 | return followerName; 1088 | } 1089 | 1090 | public void setFollowerName(String followerName) { 1091 | this.followerName = followerName; 1092 | } 1093 | 1094 | public void update(String status) { 1095 | //send message to followers that Channel is live. 1096 | } 1097 | 1098 | public void play() { 1099 | //play channel 1100 | } 1101 | 1102 | } 1103 | ``` 1104 | 1105 | # Week 3 1106 | 1107 | ## 1. Model-View-Controller Pattern 1108 | 1109 | A diagram of a simple MVC pattern 1110 | ![](https://i.imgur.com/cGlvVpi.png) 1111 | 1112 | - Java code example 1113 | Imagine you are creating an interface for a grocery store, where cashiers can enter orders, and they are displayed. Customers and cashiers should be able to see the list of items entered into the order with a barcode scanner, and see the total bill amount. Cashiers should also be able to make corrections if necessary. 1114 | 1115 | - Model 1116 | ![](https://i.imgur.com/Vv98tLY.png) 1117 | ![](https://i.imgur.com/no5bQlq.png) 1118 | - View ![](https://i.imgur.com/H7GhWnG.png) 1119 | ![](https://i.imgur.com/P2IKs9w.png) 1120 | - Controller 1121 | ![](https://i.imgur.com/qqWP8Ur.png) 1122 | 1123 | #### Assignment – MVC Pattern 1124 | 1125 | You have been asked by a local coffee shop to create a system that allows managers to see, edit and add employee information. You have decided to create them a web application. They have informed you that they may decide to grow and expand later on and would like the system to be flexible to expansion. 1126 | Create a UML class diagram that displays the basic MVC pattern for this web application. The system should keep track of an **employee’s name**, **ID number**, **job title** and **salary**. The controller should be able to get the employee model’s properties (getter methods) and change the properties (setter methods). The view should only display employee info. 1127 | ![](https://i.imgur.com/oRM6OvQ.png) 1128 | 1129 | ## 2. Design Principles Underlying Design Patterns 1130 | 1131 | ### 2.1 Liskov Substitution Principle 1132 | 1133 | If inheritance is not used correctly, it can lead to a violation of the “Liskov Substitution Principle”. This principle uses substitution to determine whether or not inheritance has been properly used. The Liskov Substitution Principle states that: 1134 | 1135 | **If a class, S, is a subtype of a class, B, then S can be used to replace all instances of B without changing the behaviors of a program.** 1136 | 1137 | #### 2.1.1 Inheritance Guidelines 1138 | 1139 | 1. The condition used to determine if a base class should or should not invoke a method cannot be "strengthened" by a subclass. That is, a subclass cannot add more conditions to determine if a method should be called. 1140 | 2. The condition of the program after the execution of a method cannot be "weakened" by a subclass. This means that the subclass should cause the state of the program to be in the same state as the base class after a method call. Subclasses are allowed to "strengthen" the postcondition of a program. For example, if the base class sets an alarm for a specific date, the subclass must do the same, but the result can be more precise by setting the specific hour as well. 1141 | 3. Invariant conditions that exist in the base class, must also remain invariant in the subclass. Since invariant conditions are expected to be immutable, the subclass should not change them as it may cause a side effect in the behaviours of the base class or the program. 1142 | 4. Immutable characteristics of a base class must not be changed by the subclass. Since classes can modify their own characteristics, a subclass can modify all the characteristics that it inherits from the base. However, the base class may encapsulate attributes that should be fixed values. These values are identifiable by observing whether or not they are changed in the program, or by a method in the base class. If it is not changed, then these attributes are considered immutable. Subclasses can get around this problem by declaring and modifying their own attributes. The attributes of a subclass are not visible to the base class and therefore, do not affect the behaviour of the base class. 1143 | 1144 | ### 2.2 Open/Close Principle 1145 | 1146 | It states that classes should be open for extension but closed to change. 1147 | 1148 | You should considered a class as being “closed” to editing once it has been: 1149 | 1150 | - Tested to be functioning properly 1151 | - All the attributes and behaviours are encapsulated 1152 | - proven to be stable within your system 1153 | 1154 | If a system needs to be extended or have more features added, then the “open” side of the principle comes into play. 1155 | 1156 | - **inheritance** of a superclass 1157 | ![enter image description here](https://i.imgur.com/zofGXrs.png) 1158 | If you want to limit a class so that it is no longer extendable, it can be declared as “final” to prevent further inheritance. 1159 | 1160 | - A class can be open, is if the class is abstract and enforces the open/close principle through polymorphism. 1161 | ![enter image description here](https://i.imgur.com/oEo8wIN.png) 1162 | 1163 | #### The Open/Close Principle 1164 | 1165 | The open/close principle is used to keep stable parts of a system separate from varying parts. While you want to be able to add more features to your system, you don't want to do it at the expense of disrupting something that works. By using extension over change, you can work on the varying parts without introducing unwanted side effects into the stable parts. 1166 | 1167 | #### The goal of object-oriented design is to: 1168 | 1169 | - Help keep a system stable by “closing” classes to change. 1170 | - Allow a system to open for extension through inheritance or Design Patterns interfaces. 1171 | 1172 | ### 2.3 Dependency Inversion Principle 1173 | 1174 | #### The Dependency Inversion Principle is a means to: 1175 | 1176 | - Change the referencing of concrete classes from being direct to indirect 1177 | - Generalize the behaviors of your concrete classes into abstract classes and interfaces 1178 | - Have client classes interact with your sysrem through a generalization rather than directly with concrete resources 1179 | - Put emphasis on high level dependency over low level concrete dependency 1180 | 1181 | ### 2.4 Composing Objects Principle 1182 | 1183 | This principle states that classes should achieve code reuse through aggregation rather than inheritance. Design patterns like the **composite design pattern** and **decorator design pattern** use this design principle. 1184 | 1185 | #### Advantages 1186 | 1187 | - Aggregation and delegation offer less coupling thant inheritance 1188 | - "Arms length" relationship 1189 | ![enter image description here](https://i.imgur.com/TkJWh9N.png) 1190 | - Provides your system with more flexibility 1191 | - Dynamically change the behaviod of objects at run time 1192 | 1193 | #### Disadvantages 1194 | 1195 | The biggest drawback of composition is that you must provide implementations for all behaviour, without the benefit of inheritance to share code. That means that you might have very similar implementation across classes. 1196 | 1197 | #### Summary 1198 | 1199 | The composing objects principle will: 1200 | 1201 | - Provide a means of code reuse without the tight coupling of inheritance. 1202 | - Allow objects to dynamically add behaviours at run time. 1203 | - Provide your system with more flexibility, so that less time needs to 1204 | be spent on system updates. 1205 | 1206 | #### Did You Know? 1207 | 1208 | Some good tips and questions to help you decide whether the best solution for your system is composition or inheritance include: 1209 | 1210 | - You need to examine the needs of your system in order to determine whihc design principle is appropriate 1211 | - Do you have a set of related classes or unrelated classes? 1212 | - What is a common behaviour between them? 1213 | - Do you need specialized classes to handle specific cases or do you need a different implementation of the same behaviour? 1214 | 1215 | ### 2.5 Interface Segregation Principle 1216 | 1217 | The interface segregation principle states that **a class should not be forced to depend on methods it does not use**. This means that any classes that implement an interface, should not have "dummy" implementation of any methods defined in the interface. Instead, you should **split large interfaces into smaller generalizations**. 1218 | 1219 | Bad example: 1220 | ![enter image description here](https://i.imgur.com/gjxRkdA.png) 1221 | 1222 | Good example: 1223 | ![enter image description here](https://i.imgur.com/CYA8fV8.png) 1224 | 1225 | Interfaces are an integral part of object oriented systems. 1226 | 1227 | #### Summary 1228 | 1229 | The interface segregation principle states that: 1230 | 1231 | - A class should not be forced to depend on methods it does not use. 1232 | - Interfaces should be split up in such a way that it can properly 1233 | describe the separate functionalities of your system. 1234 | 1235 | Remember that interfaces are descriptions of what parts of your system can do. 1236 | 1237 | ### 2.6 Principle of Least Knowledge 1238 | 1239 | To manage complexity, one idea is that **a class should be designed so that it does not need to know about and depend upon almost every other class in the system**. 1240 | 1241 | #### Law of Demeter 1242 | 1243 | The underlying idea of this law is that **classes should know about and interact with as few other classes as possible**. This means that any class should only communicate with it "immediate friends". These "friends" would be other classes that one class should only know about. 1244 | 1245 | ##### First Rule 1246 | 1247 | A method, M, in an object, O, can call on any other method within O itself. 1248 | ![enter image description here](https://i.imgur.com/Bu3ZJ5P.png) 1249 | 1250 | ##### Second Rule 1251 | 1252 | A method, M, can call the methods of any parameter P. 1253 | ![enter image description here](https://i.imgur.com/a2pMoze.png) 1254 | 1255 | ##### Third Rule 1256 | 1257 | A method, M, can call a method, N, of an object, I, if I is instantiated within M. 1258 | ![enter image description here](https://i.imgur.com/Bag4z3U.png) 1259 | 1260 | ##### Fourth Rule 1261 | 1262 | Any method, M, in object O, can invoke methods of any type of object that is a direct component of O. 1263 | ![enter image description here](https://i.imgur.com/x0AR1fg.png) 1264 | 1265 | The Law of Demeter apperas to be a complicated and abstract concept, but all the rules come down to the principle that **your should not allow a method to acces another method by "reaching through" an object**. This means that **a method should not invoke methods of any object that is not local.** 1266 | 1267 | #### “Reach Through" 1268 | 1269 | **Returned objects must of the same type as:** 1270 | 1271 | - Those declared in the method parameter. 1272 | - Those declared and instantiated locally in the method. 1273 | - Those declared in instance variable of the class that encapsulates 1274 | the method. 1275 | 1276 | **According to this design principle, a method, M, of an object should only call other methods if they are:** 1277 | 1278 | 1. Encapsulated within the same object. 1279 | 2. Encapsualted within an object that is in the parameters of M. 1280 | 3. Encapsulated within an object that is instantiated inside the M. 1281 | 4. Encapsulated within an object that is referenced in an instance 1282 | variable of the class for M. 1283 | -------------------------------------------------------------------------------- /SharingApp/.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 | -------------------------------------------------------------------------------- /SharingApp/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /SharingApp/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | //buildToolsVersion "25.0.0" 6 | defaultConfig { 7 | applicationId "com.example.sharingapp" 8 | minSdkVersion 19 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | implementation files('src/include/gson-2.8.2-SNAPSHOT.jar') 28 | implementation 'com.android.support:appcompat-v7:26.1.0' 29 | implementation 'com.android.support:support-v4:26.1.0' 30 | implementation 'com.android.support:design:26.1.0' 31 | testImplementation 'junit:junit:4.12' 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SharingApp/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 /Users/svetlanna/Library/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 | -------------------------------------------------------------------------------- /SharingApp/app/src/androidTest/java/com/example/sharingapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.sharingapp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SharingApp/app/src/include/gson-2.8.2-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akueisara/design-patterns/2215e79b3aea917cc0ffd34bf6b8fd85c81049a9/SharingApp/app/src/include/gson-2.8.2-SNAPSHOT.jar -------------------------------------------------------------------------------- /SharingApp/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/AddContactActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.EditText; 8 | 9 | /** 10 | * Add a new contact 11 | */ 12 | public class AddContactActivity extends AppCompatActivity { 13 | 14 | private ContactList contact_list = new ContactList(); 15 | private ContactListController contact_list_controller = new ContactListController(contact_list); 16 | 17 | private Context context; 18 | 19 | private EditText username; 20 | private EditText email; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_add_contact); 26 | 27 | username = (EditText) findViewById(R.id.username); 28 | email = (EditText) findViewById(R.id.email); 29 | 30 | context = getApplicationContext(); 31 | contact_list_controller.loadContacts(context); 32 | } 33 | 34 | public void saveContact(View view) { 35 | 36 | String username_str = username.getText().toString(); 37 | String email_str = email.getText().toString(); 38 | 39 | if (username_str.equals("")) { 40 | username.setError("Empty field!"); 41 | return; 42 | } 43 | 44 | if (email_str.equals("")) { 45 | email.setError("Empty field!"); 46 | return; 47 | } 48 | 49 | if (!email_str.contains("@")){ 50 | email.setError("Must be an email address!"); 51 | return; 52 | } 53 | 54 | if (!contact_list.isUsernameAvailable(username_str)){ 55 | username.setError("Username already taken!"); 56 | return; 57 | } 58 | 59 | Contact contact = new Contact(username_str, email_str, null); 60 | 61 | // Add contact 62 | boolean success = contact_list_controller.addContact(contact, context); 63 | if (!success){ 64 | return; 65 | } 66 | 67 | // End AddContactActivity 68 | finish(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/AddContactCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | public class AddContactCommand extends Command { 6 | 7 | private ContactList contact_list; 8 | private Contact contact; 9 | private Context context; 10 | 11 | public AddContactCommand(ContactList contact_list, Contact contact, Context context) { 12 | this.contact_list = contact_list; 13 | this.contact = contact; 14 | this.context = context; 15 | } 16 | 17 | public void execute(){ 18 | contact_list.addContact(contact); 19 | setIsExecuted(contact_list.saveContacts(context)); 20 | } 21 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/AddItemActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.provider.MediaStore; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.view.View; 10 | import android.widget.EditText; 11 | import android.widget.ImageView; 12 | 13 | /** 14 | * Add a new item 15 | */ 16 | public class AddItemActivity extends AppCompatActivity { 17 | 18 | private EditText title; 19 | private EditText maker; 20 | private EditText description; 21 | private EditText length; 22 | private EditText width; 23 | private EditText height; 24 | 25 | private ImageView photo; 26 | private Bitmap image; 27 | private int REQUEST_CODE = 1; 28 | 29 | private ItemList item_list = new ItemList(); 30 | private ItemListController item_list_controller = new ItemListController(item_list); 31 | 32 | private Context context; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | 38 | setContentView(R.layout.activity_add_item); 39 | 40 | title = (EditText) findViewById(R.id.title); 41 | maker = (EditText) findViewById(R.id.maker); 42 | description = (EditText) findViewById(R.id.description); 43 | length = (EditText) findViewById(R.id.length); 44 | width = (EditText) findViewById(R.id.width); 45 | height = (EditText) findViewById(R.id.height); 46 | photo = (ImageView) findViewById(R.id.image_view); 47 | 48 | photo.setImageResource(android.R.drawable.ic_menu_gallery); 49 | 50 | context = getApplicationContext(); 51 | item_list_controller.loadItems(context); 52 | } 53 | 54 | public void saveItem (View view) { 55 | 56 | String title_str = title.getText().toString(); 57 | String maker_str = maker.getText().toString(); 58 | String description_str = description.getText().toString(); 59 | String length_str = length.getText().toString(); 60 | String width_str = width.getText().toString(); 61 | String height_str = height.getText().toString(); 62 | 63 | if (title_str.equals("")) { 64 | title.setError("Empty field!"); 65 | return; 66 | } 67 | 68 | if (maker_str.equals("")) { 69 | maker.setError("Empty field!"); 70 | return; 71 | } 72 | 73 | if (description_str.equals("")) { 74 | description.setError("Empty field!"); 75 | return; 76 | } 77 | 78 | if (length_str.equals("")) { 79 | length.setError("Empty field!"); 80 | return; 81 | } 82 | 83 | if (width_str.equals("")) { 84 | width.setError("Empty field!"); 85 | return; 86 | } 87 | 88 | if (height_str.equals("")) { 89 | height.setError("Empty field!"); 90 | return; 91 | } 92 | 93 | Item item = new Item(title_str, maker_str, description_str, image, null); 94 | ItemController item_controller = new ItemController(item); 95 | item_controller.setDimensions(length_str, width_str, height_str); 96 | 97 | // Add item 98 | boolean success = item_list_controller.addItem(item, context); 99 | if (!success) { 100 | return; 101 | } 102 | 103 | // End AddItemActivity 104 | Intent intent = new Intent(this, MainActivity.class); 105 | startActivity(intent); 106 | } 107 | 108 | public void addPhoto(View view) { 109 | Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 110 | if (intent.resolveActivity(getPackageManager()) != null) { 111 | startActivityForResult(intent, REQUEST_CODE); 112 | } 113 | } 114 | 115 | public void deletePhoto(View view) { 116 | image = null; 117 | photo.setImageResource(android.R.drawable.ic_menu_gallery); 118 | } 119 | 120 | @Override 121 | protected void onActivityResult(int request_code, int result_code, Intent intent){ 122 | if (request_code == REQUEST_CODE && result_code == RESULT_OK){ 123 | Bundle extras = intent.getExtras(); 124 | image = (Bitmap) extras.get("data"); 125 | photo.setImageBitmap(image); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/AddItemCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | import android.content.Context; 3 | 4 | /** 5 | * Command to add item 6 | */ 7 | public class AddItemCommand extends Command { 8 | 9 | private ItemList item_list; 10 | private Item item; 11 | private Context context; 12 | 13 | public AddItemCommand(ItemList item_list, Item item, Context context) { 14 | this.item_list = item_list; 15 | this.item = item; 16 | this.context = context; 17 | } 18 | 19 | public void execute(){ 20 | item_list.addItem(item); 21 | setIsExecuted(item_list.saveItems(context)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/AllItemsFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Displays a list of all items 12 | */ 13 | public class AllItemsFragment extends ItemsFragment { 14 | 15 | @Override 16 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 17 | 18 | super.onCreateView(inflater, container, savedInstanceState); 19 | super.setVariables(R.layout.all_items_fragment, R.id.my_items); 20 | super.loadItems(AllItemsFragment.this); 21 | super.setFragmentOnItemLongClickListener(); 22 | 23 | return rootView; 24 | } 25 | 26 | public ArrayList filterItems() { 27 | return item_list_controller.getItems(); 28 | } 29 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/AvailableItemsFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Displays a list of all "Available" items 12 | */ 13 | public class AvailableItemsFragment extends ItemsFragment{ 14 | 15 | @Override 16 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 17 | 18 | super.onCreateView(inflater,container, savedInstanceState); 19 | super.setVariables(R.layout.available_items_fragment, R.id.my_available_items); 20 | super.loadItems(AvailableItemsFragment.this); 21 | super.setFragmentOnItemLongClickListener(); 22 | 23 | return rootView; 24 | } 25 | 26 | public ArrayList filterItems() { 27 | String status = "Available"; 28 | return item_list_controller.filterItemsByStatus(status); 29 | } 30 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/BorrowedItemsFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Displays a list of all "Borrowed" Items 12 | */ 13 | public class BorrowedItemsFragment extends ItemsFragment { 14 | 15 | @Override 16 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 17 | 18 | super.onCreateView(inflater,container, savedInstanceState); 19 | super.setVariables(R.layout.borrowed_items_fragment, R.id.my_borrowed_items); 20 | super.loadItems(BorrowedItemsFragment.this); 21 | super.setFragmentOnItemLongClickListener(); 22 | 23 | return rootView; 24 | } 25 | 26 | public ArrayList filterItems() { 27 | String status = "Borrowed"; 28 | return item_list_controller.filterItemsByStatus(status); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/Command.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | /** 4 | * Superclass of AddContactCommand, EditContactCommand, DeleteContactCommand, 5 | * AddItemCommand, EditItemCommand, DeleteItemCommand 6 | */ 7 | public abstract class Command { 8 | 9 | private boolean is_executed; 10 | 11 | public Command(){ 12 | is_executed = false; 13 | } 14 | 15 | public abstract void execute(); 16 | 17 | public boolean isExecuted(){ 18 | return is_executed; 19 | } 20 | 21 | public void setIsExecuted(boolean is_executed) { 22 | this.is_executed = is_executed; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/Contact.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * Contact class 7 | */ 8 | public class Contact extends Observable { 9 | private String username; 10 | private String email; 11 | private String id; 12 | 13 | Contact(String username, String email, String id) { 14 | this.username = username; 15 | this.email = email; 16 | 17 | if (id == null){ 18 | setId(); 19 | } else { 20 | updateId(id); 21 | } 22 | } 23 | 24 | public String getId(){ 25 | return this.id; 26 | } 27 | 28 | public void setId() { 29 | this.id = UUID.randomUUID().toString(); 30 | notifyObservers(); 31 | } 32 | 33 | public void updateId(String id){ 34 | this.id = id; 35 | notifyObservers(); 36 | } 37 | 38 | public String getUsername() { 39 | return username; 40 | } 41 | 42 | public void setUsername(String username) { 43 | this.username = username; 44 | notifyObservers(); 45 | } 46 | 47 | public String getEmail() { 48 | return email; 49 | } 50 | 51 | public void setEmail(String email) { 52 | this.email = email; 53 | notifyObservers(); 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ContactAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ArrayAdapter; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import java.util.ArrayList; 12 | 13 | /** 14 | * ContactAdapter is responsible for what information is displayed in ListView entries. 15 | */ 16 | public class ContactAdapter extends ArrayAdapter { 17 | 18 | private LayoutInflater inflater; 19 | private Context context; 20 | 21 | public ContactAdapter(Context context, ArrayList contacts) { 22 | super(context, 0, contacts); 23 | this.context = context; 24 | this.inflater = LayoutInflater.from(context); 25 | } 26 | 27 | @Override 28 | public View getView(int position, View convertView, ViewGroup parent) { 29 | 30 | // getItem(position) gets the "contact" at "position" in the "contacts" ArrayList 31 | // (where "contacts" is a parameter in the ContactAdapter creator as seen above ^^) 32 | Contact contact = getItem(position); 33 | 34 | String username = "Username: " + contact.getUsername(); 35 | String email = "Email: " + contact.getEmail(); 36 | 37 | // Check if an existing view is being reused, otherwise inflate the view. 38 | if (convertView == null) { 39 | convertView = inflater.from(context).inflate(R.layout.contactlist_contact, parent, false); 40 | } 41 | 42 | TextView username_tv = (TextView) convertView.findViewById(R.id.username_tv); 43 | TextView email_tv = (TextView) convertView.findViewById(R.id.email_tv); 44 | ImageView photo = (ImageView) convertView.findViewById(R.id.contacts_image_view); 45 | 46 | photo.setImageResource(android.R.drawable.ic_menu_gallery); 47 | 48 | username_tv.setText(username); 49 | email_tv.setText(email); 50 | 51 | return convertView; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ContactController.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | public class ContactController { 4 | 5 | private Contact contact; 6 | 7 | public ContactController(Contact contact){ 8 | this.contact = contact; 9 | } 10 | 11 | public String getId(){ 12 | return contact.getId(); 13 | } 14 | 15 | public void setId() { 16 | contact.setId(); 17 | } 18 | 19 | public void setUsername(String username) { 20 | contact.setUsername(username); 21 | } 22 | 23 | public String getUsername() { 24 | return contact.getUsername(); 25 | } 26 | 27 | public void setEmail(String email) { 28 | contact.setEmail(email); 29 | } 30 | 31 | public String getEmail() { 32 | return contact.getEmail(); 33 | } 34 | 35 | public Contact getContact() { return this.contact; } 36 | 37 | public void addObserver(Observer observer) { 38 | contact.addObserver(observer); 39 | } 40 | 41 | public void removeObserver(Observer observer) { 42 | contact.removeObserver(observer); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ContactList.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | import com.google.gson.Gson; 6 | import com.google.gson.reflect.TypeToken; 7 | 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStreamWriter; 14 | import java.lang.reflect.Type; 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * ContactList class 19 | */ 20 | public class ContactList extends Observable { 21 | 22 | private static ArrayList contacts; 23 | private String FILENAME = "contacts.sav"; 24 | 25 | public ContactList() { 26 | contacts = new ArrayList(); 27 | } 28 | 29 | public void setContacts(ArrayList contact_list) { 30 | contacts = contact_list; 31 | notifyObservers(); 32 | } 33 | 34 | public ArrayList getContacts() { 35 | return contacts; 36 | } 37 | 38 | public ArrayList getAllUsernames(){ 39 | ArrayList username_list = new ArrayList(); 40 | for (Contact u : contacts){ 41 | username_list.add(u.getUsername()); 42 | } 43 | return username_list; 44 | } 45 | 46 | public void addContact(Contact contact) { 47 | contacts.add(contact); 48 | notifyObservers(); 49 | } 50 | 51 | public void deleteContact(Contact contact) { 52 | contacts.remove(contact); 53 | notifyObservers(); 54 | } 55 | 56 | public Contact getContact(int index) { 57 | return contacts.get(index); 58 | } 59 | 60 | public int getSize() { 61 | return contacts.size(); 62 | } 63 | 64 | public Contact getContactByUsername(String username){ 65 | for (Contact c : contacts){ 66 | if (c.getUsername().equals(username)){ 67 | return c; 68 | } 69 | } 70 | return null; 71 | } 72 | 73 | public boolean hasContact(Contact contact) { 74 | for (Contact c : contacts) { 75 | if (contact.getId().equals(c.getId())) { 76 | return true; 77 | } 78 | } 79 | return false; 80 | } 81 | 82 | public int getIndex(Contact contact) { 83 | int pos = 0; 84 | for (Contact c : contacts) { 85 | if (contact.getId().equals(c.getId())) { 86 | return pos; 87 | } 88 | pos = pos+1; 89 | } 90 | return -1; 91 | } 92 | 93 | public boolean isUsernameAvailable(String username){ 94 | for (Contact c : contacts) { 95 | if (c.getUsername().equals(username)) { 96 | return false; 97 | } 98 | } 99 | return true; 100 | } 101 | 102 | public void loadContacts(Context context) { 103 | 104 | try { 105 | FileInputStream fis = context.openFileInput(FILENAME); 106 | InputStreamReader isr = new InputStreamReader(fis); 107 | Gson gson = new Gson(); 108 | Type listType = new TypeToken>() {}.getType(); 109 | contacts = gson.fromJson(isr, listType); // temporary 110 | fis.close(); 111 | } catch (FileNotFoundException e) { 112 | contacts = new ArrayList(); 113 | } catch (IOException e) { 114 | contacts = new ArrayList(); 115 | } 116 | notifyObservers(); 117 | } 118 | 119 | public boolean saveContacts(Context context) { 120 | try { 121 | FileOutputStream fos = context.openFileOutput(FILENAME, 0); 122 | OutputStreamWriter osw = new OutputStreamWriter(fos); 123 | Gson gson = new Gson(); 124 | gson.toJson(contacts, osw); 125 | osw.flush(); 126 | fos.close(); 127 | } catch (FileNotFoundException e) { 128 | e.printStackTrace(); 129 | return false; 130 | } catch (IOException e) { 131 | e.printStackTrace(); 132 | return false; 133 | } 134 | return true; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ContactListController.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | 7 | public class ContactListController { 8 | 9 | private ContactList contact_list; 10 | 11 | public ContactListController(ContactList contact_list){ 12 | this.contact_list = contact_list; 13 | } 14 | 15 | public void setContacts(ArrayList contact_list) { 16 | this.contact_list.setContacts(contact_list); 17 | } 18 | 19 | public ArrayList getContacts() { 20 | return contact_list.getContacts(); 21 | } 22 | 23 | public boolean addContact(Contact contact, Context context){ 24 | AddContactCommand add_contact_command = new AddContactCommand(contact_list, contact, context); 25 | add_contact_command.execute(); 26 | return add_contact_command.isExecuted(); 27 | } 28 | 29 | public boolean deleteContact(Contact contact, Context context) { 30 | DeleteContactCommand delete_contact_command = new DeleteContactCommand(contact_list, contact, context); 31 | delete_contact_command.execute(); 32 | return delete_contact_command.isExecuted(); 33 | } 34 | 35 | public boolean editContact(Contact contact, Contact updated_contact, Context context){ 36 | EditContactCommand edit_contact_command = new EditContactCommand(contact_list, contact, updated_contact, context); 37 | edit_contact_command.execute(); 38 | return edit_contact_command.isExecuted(); 39 | } 40 | 41 | public Contact getContact(int index) { 42 | return contact_list.getContact(index); 43 | } 44 | 45 | public int getIndex(Contact contact) { 46 | return contact_list.getIndex(contact); 47 | } 48 | 49 | public boolean isUsernameAvailable(String username){ 50 | return contact_list.isUsernameAvailable(username); 51 | } 52 | 53 | public int getSize() { 54 | return contact_list.getSize(); 55 | } 56 | 57 | public void loadContacts(Context context) { 58 | contact_list.loadContacts(context); 59 | } 60 | 61 | public ArrayList getAllUsernames() { 62 | return contact_list.getAllUsernames(); 63 | } 64 | 65 | public Contact getContactByUsername(String username) { 66 | return contact_list.getContactByUsername(username); 67 | } 68 | 69 | public boolean hasContact(Contact contact) { 70 | return contact_list.hasContact(contact); 71 | } 72 | 73 | public void addObserver(Observer observer) { 74 | contact_list.addObserver(observer); 75 | } 76 | 77 | public void removeObserver(Observer observer) { 78 | contact_list.removeObserver(observer); 79 | } 80 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ContactsActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.AdapterView; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ListView; 11 | import android.widget.Toast; 12 | 13 | /** 14 | * Displays a list of all contacts 15 | * Note: You will not be able edit/delete contacts which are "active" borrowers 16 | */ 17 | public class ContactsActivity extends AppCompatActivity implements Observer { 18 | 19 | private ContactList contact_list = new ContactList(); 20 | private ContactListController contact_list_controller = new ContactListController(contact_list); 21 | 22 | private ContactList active_borrowers_list = new ContactList(); 23 | private ContactListController active_borrowers_list_controller = new ContactListController(active_borrowers_list); 24 | 25 | private ItemList item_list = new ItemList(); 26 | private ItemListController item_list_controller = new ItemListController(item_list); 27 | 28 | private ListView my_contacts; 29 | private ArrayAdapter adapter; 30 | private Context context; 31 | 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_contacts); 35 | 36 | context = getApplicationContext(); 37 | 38 | contact_list_controller.addObserver(this); 39 | contact_list_controller.loadContacts(context); 40 | item_list_controller.loadItems(context); 41 | 42 | // When contact is long clicked, this starts EditContactActivity 43 | my_contacts.setOnItemLongClickListener(new android.widget.AdapterView.OnItemLongClickListener() { 44 | 45 | @Override 46 | public boolean onItemLongClick(AdapterView parent, View view, int pos, long id) { 47 | 48 | Contact contact = adapter.getItem(pos); 49 | 50 | // Do not allow an "active" borrower to be edited 51 | active_borrowers_list_controller.setContacts(item_list_controller.getActiveBorrowers()); 52 | if (active_borrowers_list_controller != null) { 53 | if (active_borrowers_list_controller.hasContact(contact)) { 54 | CharSequence text = "Cannot edit or delete active borrower!"; 55 | int duration = Toast.LENGTH_SHORT; 56 | Toast.makeText(context, text, duration).show(); 57 | return true; 58 | } 59 | } 60 | 61 | contact_list_controller.loadContacts(context); // must load contacts again here 62 | int meta_pos = contact_list_controller.getIndex(contact); 63 | 64 | Intent intent = new Intent(context, EditContactActivity.class); 65 | intent.putExtra("position", meta_pos); 66 | startActivity(intent); 67 | 68 | return true; 69 | } 70 | }); 71 | } 72 | 73 | @Override 74 | protected void onStart() { 75 | super.onStart(); 76 | context = getApplicationContext(); 77 | contact_list_controller.loadContacts(context); 78 | } 79 | 80 | public void addContactActivity(View view){ 81 | Intent intent = new Intent(this, AddContactActivity.class); 82 | startActivity(intent); 83 | } 84 | 85 | /** 86 | * Called when the activity is destroyed, thus we remove this activity as a listener 87 | */ 88 | @Override 89 | protected void onDestroy() { 90 | super.onDestroy(); 91 | contact_list_controller.removeObserver(this); 92 | } 93 | 94 | /** 95 | * Update the view 96 | */ 97 | @Override 98 | public void update(){ 99 | my_contacts = (ListView) findViewById(R.id.my_contacts); 100 | adapter = new ContactAdapter(ContactsActivity.this, contact_list_controller.getContacts()); 101 | my_contacts.setAdapter(adapter); 102 | adapter.notifyDataSetChanged(); 103 | } 104 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/DeleteContactCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | public class DeleteContactCommand extends Command { 6 | 7 | private ContactList contact_list; 8 | private Contact contact; 9 | private Context context; 10 | 11 | public DeleteContactCommand(ContactList contact_list, Contact contact, Context context) { 12 | this.contact_list = contact_list; 13 | this.contact = contact; 14 | this.context = context; 15 | } 16 | 17 | public void execute() { 18 | contact_list.deleteContact(contact); 19 | setIsExecuted(contact_list.saveContacts(context)); 20 | } 21 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/DeleteItemCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | import android.content.Context; 3 | 4 | /** 5 | * Command to delete an item 6 | */ 7 | public class DeleteItemCommand extends Command { 8 | private ItemList item_list; 9 | private Item item; 10 | private Context context; 11 | 12 | public DeleteItemCommand(ItemList item_list, Item item, Context context) { 13 | this.item_list = item_list; 14 | this.item = item; 15 | this.context = context; 16 | } 17 | 18 | public void execute() { 19 | item_list.deleteItem(item); 20 | setIsExecuted(item_list.saveItems(context)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/Dimensions.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | /** 4 | * Dimensions class 5 | */ 6 | public class Dimensions { 7 | 8 | private String length; 9 | private String width; 10 | private String height; 11 | 12 | public Dimensions(String length, String width, String height) { 13 | this.length = length; 14 | this.width = width; 15 | this.height = height; 16 | } 17 | 18 | public String getLength() { 19 | return length; 20 | } 21 | 22 | public String getWidth() { 23 | return width; 24 | } 25 | 26 | public String getHeight() { 27 | return height; 28 | } 29 | 30 | public String getDimensions() { 31 | return length + " x " + width + " x " + height; 32 | } 33 | 34 | public void setDimensions(String length, String width, String height) { 35 | this.length = length; 36 | this.width = width; 37 | this.height = height; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/EditContactActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.EditText; 9 | 10 | /** 11 | * Editing a pre-existing contact consists of deleting the old contact and adding a new contact with the old 12 | * contact's id. 13 | * Note: You will not be able contacts which are "active" borrowers 14 | */ 15 | public class EditContactActivity extends AppCompatActivity implements Observer { 16 | 17 | private ContactList contact_list = new ContactList(); 18 | private ContactListController contact_list_controller = new ContactListController(contact_list); 19 | 20 | private Contact contact; 21 | private ContactController contact_controller = new ContactController(contact); 22 | 23 | private EditText email; 24 | private EditText username; 25 | private Context context; 26 | 27 | private boolean on_create_update = false; 28 | private int pos; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_edit_contact); 34 | 35 | context = getApplicationContext(); 36 | contact_list_controller.loadContacts(context); 37 | 38 | Intent intent = getIntent(); 39 | pos = intent.getIntExtra("position", 0); 40 | 41 | username = (EditText) findViewById(R.id.username); 42 | email = (EditText) findViewById(R.id.email); 43 | 44 | on_create_update = true; 45 | 46 | contact_list_controller.addObserver(this); 47 | contact_list_controller.loadContacts(context); 48 | 49 | on_create_update = false; 50 | } 51 | 52 | public void saveContact(View view) { 53 | 54 | String email_str = email.getText().toString(); 55 | 56 | if (email_str.equals("")) { 57 | email.setError("Empty field!"); 58 | return; 59 | } 60 | 61 | if (!email_str.contains("@")){ 62 | email.setError("Must be an email address!"); 63 | return; 64 | } 65 | 66 | String username_str = username.getText().toString(); 67 | String id = contact_controller.getId(); // Reuse the contact id 68 | 69 | // Check that username is unique AND username is changed (Note: if username was not changed 70 | // then this should be fine, because it was already unique.) 71 | if (!contact_list_controller.isUsernameAvailable(username_str) && !(contact_controller.getUsername().equals(username_str))) { 72 | username.setError("Username already taken!"); 73 | return; 74 | } 75 | 76 | Contact updated_contact = new Contact(username_str, email_str, id); 77 | 78 | // Edit item 79 | boolean success = contact_list_controller.editContact(contact, updated_contact, context); 80 | if (!success){ 81 | return; 82 | } 83 | 84 | // End EditContactActivity 85 | contact_list_controller.removeObserver(this); 86 | 87 | finish(); 88 | } 89 | 90 | public void deleteContact(View view) { 91 | 92 | // Delete contact 93 | boolean success = contact_list_controller.deleteContact(contact, context); 94 | if (!success){ 95 | return; 96 | } 97 | 98 | // End EditContactActivity 99 | contact_list_controller.removeObserver(this); 100 | 101 | finish(); 102 | } 103 | 104 | @Override 105 | public void update() { 106 | if (on_create_update){ 107 | contact = contact_list_controller.getContact(pos); 108 | 109 | username.setText(contact_controller.getUsername()); 110 | email.setText(contact_controller.getEmail()); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/EditContactCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | public class EditContactCommand extends Command { 6 | private ContactList contact_list; 7 | private Contact old_contact; 8 | private Contact new_contact; 9 | private Context context; 10 | 11 | public EditContactCommand(ContactList contact_list, Contact old_contact, Contact new_contact, Context context) { 12 | this.contact_list = contact_list; 13 | this.old_contact = old_contact; 14 | this.new_contact = new_contact; 15 | this.context = context; 16 | } 17 | 18 | public void execute() { 19 | contact_list.deleteContact(old_contact); 20 | contact_list.addContact(new_contact); 21 | setIsExecuted(contact_list.saveContacts(context)); 22 | } 23 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/EditItemActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.provider.MediaStore; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.view.View; 10 | import android.widget.ArrayAdapter; 11 | import android.widget.EditText; 12 | import android.widget.ImageView; 13 | import android.widget.Spinner; 14 | import android.widget.Switch; 15 | import android.widget.TextView; 16 | 17 | /** 18 | * Editing a pre-existing item consists of deleting the old item and adding a new item with the old 19 | * item's id. 20 | * Note: invisible EditText is used to setError for status. For whatever reason we cannot .setError to 21 | * the status Switch so instead an error is set to an "invisible" EditText. 22 | */ 23 | public class EditItemActivity extends AppCompatActivity implements Observer { 24 | 25 | private ItemList item_list = new ItemList(); 26 | private ItemListController item_list_controller = new ItemListController(item_list); 27 | 28 | private Item item; 29 | private ItemController item_controller; 30 | 31 | private Context context; 32 | 33 | private ContactList contact_list = new ContactList(); 34 | private ContactListController contact_list_controller = new ContactListController(contact_list); 35 | 36 | private Bitmap image; 37 | private int REQUEST_CODE = 1; 38 | private ImageView photo; 39 | 40 | private EditText title; 41 | private EditText maker; 42 | private EditText description; 43 | private EditText length; 44 | private EditText width; 45 | private EditText height; 46 | private Spinner borrower_spinner; 47 | private TextView borrower_tv; 48 | private Switch status; 49 | private EditText invisible; 50 | 51 | private ArrayAdapter adapter; 52 | private boolean on_create_update = false; 53 | private int pos; 54 | 55 | @Override 56 | protected void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | setContentView(R.layout.activity_edit_item); 59 | 60 | title = (EditText) findViewById(R.id.title); 61 | maker = (EditText) findViewById(R.id.maker); 62 | description = (EditText) findViewById(R.id.description); 63 | length = (EditText) findViewById(R.id.length); 64 | width = (EditText) findViewById(R.id.width); 65 | height = (EditText) findViewById(R.id.height); 66 | borrower_spinner = (Spinner) findViewById(R.id.borrower_spinner); 67 | borrower_tv = (TextView) findViewById(R.id.borrower_tv); 68 | photo = (ImageView) findViewById(R.id.image_view); 69 | status = (Switch) findViewById(R.id.available_switch); 70 | invisible = (EditText) findViewById(R.id.invisible); 71 | 72 | invisible.setVisibility(View.GONE); 73 | 74 | Intent intent = getIntent(); // Get intent from ItemsFragment 75 | pos = intent.getIntExtra("position", 0); 76 | 77 | context = getApplicationContext(); 78 | 79 | item_list_controller.addObserver(this); 80 | item_list_controller.loadItems(context); 81 | 82 | on_create_update = true; 83 | 84 | contact_list_controller.addObserver(this); 85 | contact_list_controller.loadContacts(context); 86 | 87 | on_create_update = false; 88 | } 89 | 90 | public void addPhoto(View view) { 91 | Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 92 | if (intent.resolveActivity(getPackageManager()) != null) { 93 | startActivityForResult(intent, REQUEST_CODE); 94 | } 95 | } 96 | 97 | public void deletePhoto(View view) { 98 | image = null; 99 | photo.setImageResource(android.R.drawable.ic_menu_gallery); 100 | } 101 | 102 | @Override 103 | protected void onActivityResult(int request_code, int result_code, Intent intent){ 104 | if (request_code == REQUEST_CODE && result_code == RESULT_OK){ 105 | Bundle extras = intent.getExtras(); 106 | image = (Bitmap) extras.get("data"); 107 | photo.setImageBitmap(image); 108 | } 109 | } 110 | 111 | public void deleteItem(View view) { 112 | 113 | // Delete item 114 | boolean success = item_list_controller.deleteItem(item, context); 115 | if (!success) { 116 | return; 117 | } 118 | 119 | // End EditItemActivity 120 | item_list_controller.removeObserver(this); 121 | 122 | Intent intent = new Intent(this, MainActivity.class); 123 | startActivity(intent); 124 | } 125 | 126 | public void saveItem(View view) { 127 | 128 | String title_str = title.getText().toString(); 129 | String maker_str = maker.getText().toString(); 130 | String description_str = description.getText().toString(); 131 | String length_str = length.getText().toString(); 132 | String width_str = width.getText().toString(); 133 | String height_str = height.getText().toString(); 134 | 135 | Contact contact = null; 136 | if (!status.isChecked()) { 137 | String borrower_str = borrower_spinner.getSelectedItem().toString(); 138 | contact = contact_list_controller.getContactByUsername(borrower_str); 139 | } 140 | 141 | if (title_str.equals("")) { 142 | title.setError("Empty field!"); 143 | return; 144 | } 145 | 146 | if (maker_str.equals("")) { 147 | maker.setError("Empty field!"); 148 | return; 149 | } 150 | 151 | if (description_str.equals("")) { 152 | description.setError("Empty field!"); 153 | return; 154 | } 155 | 156 | if (length_str.equals("")) { 157 | length.setError("Empty field!"); 158 | return; 159 | } 160 | 161 | if (width_str.equals("")) { 162 | width.setError("Empty field!"); 163 | return; 164 | } 165 | 166 | if (height_str.equals("")) { 167 | height.setError("Empty field!"); 168 | return; 169 | } 170 | 171 | String id = item_controller.getId(); // Reuse the item id 172 | Item updated_item = new Item(title_str, maker_str, description_str, image, id); 173 | ItemController updated_item_controller = new ItemController(updated_item); 174 | updated_item_controller.setDimensions(length_str, width_str, height_str); 175 | 176 | boolean checked = status.isChecked(); 177 | if (!checked) { 178 | updated_item_controller.setStatus("Borrowed"); 179 | updated_item_controller.setBorrower(contact); 180 | } 181 | 182 | // Edit item 183 | boolean success = item_list_controller.editItem(item, updated_item, context); 184 | if (!success) { 185 | return; 186 | } 187 | 188 | // End EditItemActivity 189 | item_list_controller.removeObserver(this); 190 | 191 | Intent intent = new Intent(this, MainActivity.class); 192 | startActivity(intent); 193 | } 194 | 195 | /** 196 | * Checked == "Available" 197 | * Unchecked == "Borrowed" 198 | */ 199 | public void toggleSwitch(View view){ 200 | if (status.isChecked()) { 201 | // Means was previously borrowed, switch was toggled to available 202 | borrower_spinner.setVisibility(View.GONE); 203 | borrower_tv.setVisibility(View.GONE); 204 | item_controller.setBorrower(null); 205 | item_controller.setStatus("Available"); 206 | 207 | } else { 208 | // Means not borrowed 209 | if (contact_list.getSize()==0){ 210 | // No contacts, need to add contacts to be able to add a borrower 211 | invisible.setEnabled(false); 212 | invisible.setVisibility(View.VISIBLE); 213 | invisible.requestFocus(); 214 | invisible.setError("No contacts available! Must add borrower to contacts."); 215 | status.setChecked(true); // Set switch to available 216 | 217 | } else { 218 | // Means was previously available 219 | borrower_spinner.setVisibility(View.VISIBLE); 220 | borrower_tv.setVisibility(View.VISIBLE); 221 | } 222 | } 223 | } 224 | 225 | /** 226 | * Only need to update the view from the onCreate method 227 | */ 228 | @Override 229 | public void update() { 230 | if (on_create_update){ 231 | adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, 232 | contact_list_controller.getAllUsernames()); 233 | borrower_spinner.setAdapter(adapter); 234 | 235 | item = item_list_controller.getItem(pos); 236 | item_controller = new ItemController(item); 237 | 238 | Contact contact = item_controller.getBorrower(); 239 | if (contact != null){ 240 | int contact_pos = contact_list_controller.getIndex(contact); 241 | borrower_spinner.setSelection(contact_pos); 242 | } 243 | 244 | title.setText(item_controller.getTitle()); 245 | maker.setText(item_controller.getMaker()); 246 | description.setText(item_controller.getDescription()); 247 | 248 | length.setText(item_controller.getLength()); 249 | width.setText(item_controller.getWidth()); 250 | height.setText(item_controller.getHeight()); 251 | 252 | String status_str = item_controller.getStatus(); 253 | if (status_str.equals("Borrowed")) { 254 | status.setChecked(false); 255 | } else { 256 | borrower_tv.setVisibility(View.GONE); 257 | borrower_spinner.setVisibility(View.GONE); 258 | } 259 | 260 | image = item_controller.getImage(); 261 | if (image != null) { 262 | photo.setImageBitmap(image); 263 | } else { 264 | photo.setImageResource(android.R.drawable.ic_menu_gallery); 265 | } 266 | } 267 | } 268 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/EditItemCommand.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | import android.content.Context; 3 | 4 | /** 5 | * Command to edit a pre-existing item 6 | */ 7 | public class EditItemCommand extends Command { 8 | private ItemList item_list; 9 | private Item old_item; 10 | private Item new_item; 11 | private Context context; 12 | 13 | public EditItemCommand(ItemList item_list, Item old_item, Item new_item, Context context) { 14 | this.item_list = item_list; 15 | this.old_item = old_item; 16 | this.new_item = new_item; 17 | this.context = context; 18 | } 19 | 20 | public void execute() { 21 | item_list.deleteItem(old_item); 22 | item_list.addItem(new_item); 23 | setIsExecuted(item_list.saveItems(context)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/Item.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.util.Base64; 6 | 7 | import java.io.ByteArrayOutputStream; 8 | import java.util.UUID; 9 | 10 | /** 11 | * Item class 12 | */ 13 | public class Item extends Observable { 14 | 15 | private String title; 16 | private String maker; 17 | private String description; 18 | private Dimensions dimensions; 19 | private String status; 20 | private Contact borrower; 21 | protected transient Bitmap image; 22 | protected String image_base64; 23 | private String id; 24 | 25 | public Item(String title, String maker, String description, Bitmap image, 26 | String id ) { 27 | this.title = title; 28 | this.maker = maker; 29 | this.description = description; 30 | this.dimensions = null; 31 | this.status = "Available"; 32 | this.borrower = null; 33 | addImage(image); 34 | 35 | if (id == null){ 36 | setId(); 37 | } else { 38 | updateId(id); 39 | } 40 | } 41 | 42 | public String getId(){ 43 | return this.id; 44 | } 45 | 46 | public void setId() { 47 | this.id = UUID.randomUUID().toString(); 48 | notifyObservers(); 49 | } 50 | 51 | public void updateId(String id){ 52 | this.id = id; 53 | notifyObservers(); 54 | } 55 | 56 | public void setTitle(String title) { 57 | this.title = title; 58 | notifyObservers(); 59 | } 60 | 61 | public String getTitle() { 62 | return title; 63 | } 64 | 65 | public void setMaker(String maker) { 66 | this.maker = maker; 67 | notifyObservers(); 68 | } 69 | 70 | public String getMaker() { 71 | return maker; 72 | } 73 | 74 | public void setDescription(String description) { 75 | this.description = description; 76 | notifyObservers(); 77 | } 78 | 79 | public String getDescription() { 80 | return description; 81 | } 82 | 83 | public void setDimensions(String length, String width, String height) { 84 | this.dimensions = new Dimensions(length, width, height); 85 | notifyObservers(); 86 | } 87 | 88 | public String getLength(){ 89 | return dimensions.getLength(); 90 | } 91 | 92 | public String getWidth(){ 93 | return dimensions.getWidth(); 94 | } 95 | 96 | public String getHeight(){ 97 | return dimensions.getHeight(); 98 | } 99 | 100 | public void setStatus(String status) { 101 | this.status = status; 102 | notifyObservers(); 103 | } 104 | 105 | public String getStatus() { 106 | return status; 107 | } 108 | 109 | public void setBorrower(Contact borrower) { 110 | this.borrower = borrower; 111 | notifyObservers(); 112 | } 113 | 114 | public Contact getBorrower() { 115 | return borrower; 116 | } 117 | 118 | public void addImage(Bitmap new_image){ 119 | if (new_image != null) { 120 | image = new_image; 121 | ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream(); 122 | new_image.compress(Bitmap.CompressFormat.PNG, 100, byteArrayBitmapStream); 123 | 124 | byte[] b = byteArrayBitmapStream.toByteArray(); 125 | image_base64 = Base64.encodeToString(b, Base64.DEFAULT); 126 | } 127 | notifyObservers(); 128 | } 129 | 130 | public Bitmap getImage(){ 131 | if (image == null && image_base64 != null) { 132 | byte[] decodeString = Base64.decode(image_base64, Base64.DEFAULT); 133 | image = BitmapFactory.decodeByteArray(decodeString, 0, decodeString.length); 134 | notifyObservers(); 135 | } 136 | return image; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ItemAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import java.util.ArrayList; 14 | 15 | /** 16 | * ItemAdapter is responsible for what information is displayed in ListView entries. 17 | */ 18 | public class ItemAdapter extends ArrayAdapter { 19 | 20 | private LayoutInflater inflater; 21 | private Fragment fragment; 22 | private Context context; 23 | 24 | public ItemAdapter(Context context, ArrayList items, Fragment fragment) { 25 | super(context, 0, items); 26 | this.context = context; 27 | this.inflater = LayoutInflater.from(context); 28 | this.fragment = fragment; 29 | } 30 | 31 | @Override 32 | public View getView(int position, View convertView, ViewGroup parent) { 33 | 34 | // getItem(position) gets the "item" at "position" in the "items" ArrayList 35 | // (where "items" is a parameter in the ItemAdapter creator as seen above ^^) 36 | Item item = getItem(position); 37 | ItemController item_controller = new ItemController(item); 38 | 39 | String title = "Title: " + item_controller.getTitle(); 40 | String description = "Description: " + item_controller.getDescription(); 41 | Bitmap thumbnail = item_controller.getImage(); 42 | String status = "Status: " + item_controller.getStatus(); 43 | 44 | // Check if an existing view is being reused, otherwise inflate the view. 45 | if (convertView == null) { 46 | convertView = inflater.from(context).inflate(R.layout.itemlist_item, parent, false); 47 | } 48 | 49 | TextView title_tv = (TextView) convertView.findViewById(R.id.title_tv); 50 | TextView status_tv = (TextView) convertView.findViewById(R.id.status_tv); 51 | TextView description_tv = (TextView) convertView.findViewById(R.id.description_tv); 52 | ImageView photo = (ImageView) convertView.findViewById(R.id.image_view); 53 | 54 | if (thumbnail != null) { 55 | photo.setImageBitmap(thumbnail); 56 | } else { 57 | photo.setImageResource(android.R.drawable.ic_menu_gallery); 58 | } 59 | 60 | title_tv.setText(title); 61 | description_tv.setText(description); 62 | 63 | // AllItemFragments: itemlist item shows title, description and status 64 | if (fragment instanceof AllItemsFragment ) { 65 | status_tv.setText(status); 66 | } 67 | 68 | // BorrowedItemsFragment/AvailableItemsFragment: itemlist item shows title and description only 69 | if (fragment instanceof BorrowedItemsFragment || fragment instanceof AvailableItemsFragment) { 70 | status_tv.setVisibility(View.GONE); 71 | } 72 | 73 | return convertView; 74 | } 75 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ItemController.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | /** 6 | * ItemController is responsible for all communication between views and Item object 7 | */ 8 | public class ItemController { 9 | 10 | private Item item; 11 | 12 | public ItemController(Item item){ 13 | this.item = item; 14 | } 15 | 16 | public String getId(){ 17 | return item.getId(); 18 | } 19 | 20 | public void setId() { 21 | item.setId(); 22 | } 23 | 24 | public void setTitle(String title) { 25 | item.setTitle(title); 26 | } 27 | 28 | public String getTitle() { 29 | return item.getTitle(); 30 | } 31 | 32 | public void setMaker(String maker) { 33 | item.setMaker(maker); 34 | } 35 | 36 | public String getMaker() { 37 | return item.getMaker(); 38 | } 39 | 40 | public void setDescription(String description) { 41 | item.setDescription(description); 42 | } 43 | 44 | public String getDescription() { 45 | return item.getDescription(); 46 | } 47 | 48 | public void setDimensions(String length, String width, String height) { 49 | item.setDimensions(length, width, height); 50 | } 51 | 52 | public String getLength() { 53 | return item.getLength(); 54 | } 55 | 56 | public String getWidth(){ 57 | return item.getWidth(); 58 | } 59 | 60 | public String getHeight(){ 61 | return item.getHeight(); 62 | } 63 | 64 | public void setStatus(String status) { 65 | item.setStatus(status); 66 | } 67 | 68 | public String getStatus() { 69 | return item.getStatus(); 70 | } 71 | 72 | public void setBorrower(Contact borrower) { 73 | item.setBorrower(borrower); 74 | } 75 | 76 | public Contact getBorrower() { 77 | return item.getBorrower(); 78 | } 79 | 80 | public void addImage(Bitmap new_image){ 81 | item.addImage(new_image); 82 | } 83 | 84 | public Bitmap getImage(){ 85 | return item.getImage(); 86 | } 87 | 88 | public Item getItem() { return this.item; } 89 | 90 | public void addObserver(Observer observer) { 91 | item.addObserver(observer); 92 | } 93 | 94 | public void removeObserver(Observer observer) { 95 | item.removeObserver(observer); 96 | } 97 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ItemList.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | import com.google.gson.Gson; 6 | import com.google.gson.reflect.TypeToken; 7 | 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStreamWriter; 14 | import java.lang.reflect.Type; 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * ItemList class 19 | */ 20 | public class ItemList extends Observable { 21 | private static ArrayList items; 22 | private String FILENAME = "items.sav"; 23 | 24 | public ItemList() { 25 | items = new ArrayList(); 26 | } 27 | 28 | public void setItems(ArrayList item_list) { 29 | items = item_list; 30 | notifyObservers(); 31 | } 32 | 33 | public ArrayList getItems() { 34 | return items; 35 | } 36 | 37 | public void addItem(Item item) { 38 | items.add(item); 39 | notifyObservers(); 40 | } 41 | 42 | public void deleteItem(Item item) { 43 | items.remove(item); 44 | notifyObservers(); 45 | } 46 | 47 | public Item getItem(int index) { 48 | return items.get(index); 49 | } 50 | 51 | public int getIndex(Item item) { 52 | int pos = 0; 53 | for (Item i : items) { 54 | if (item.getId().equals(i.getId())) { 55 | return pos; 56 | } 57 | pos = pos + 1; 58 | } 59 | return -1; 60 | } 61 | 62 | public int getSize() { 63 | return items.size(); 64 | } 65 | 66 | public void loadItems(Context context) { 67 | 68 | try { 69 | FileInputStream fis = context.openFileInput(FILENAME); 70 | InputStreamReader isr = new InputStreamReader(fis); 71 | Gson gson = new Gson(); 72 | Type listType = new TypeToken>() { 73 | }.getType(); 74 | items = gson.fromJson(isr, listType); // temporary 75 | fis.close(); 76 | } catch (FileNotFoundException e) { 77 | items = new ArrayList(); 78 | } catch (IOException e) { 79 | items = new ArrayList(); 80 | } 81 | notifyObservers(); 82 | } 83 | 84 | public boolean saveItems(Context context) { 85 | try { 86 | FileOutputStream fos = context.openFileOutput(FILENAME, 0); 87 | OutputStreamWriter osw = new OutputStreamWriter(fos); 88 | Gson gson = new Gson(); 89 | gson.toJson(items, osw); 90 | osw.flush(); 91 | fos.close(); 92 | } catch (FileNotFoundException e) { 93 | e.printStackTrace(); 94 | return false; 95 | } catch (IOException e) { 96 | e.printStackTrace(); 97 | return false; 98 | } 99 | return true; 100 | } 101 | 102 | public ArrayList getActiveBorrowers() { 103 | 104 | ArrayList active_borrowers = new ArrayList(); 105 | for (Item i : items) { 106 | Contact borrower = i.getBorrower(); 107 | if (borrower != null) { 108 | active_borrowers.add(borrower); 109 | } 110 | } 111 | return active_borrowers; 112 | } 113 | 114 | public ArrayList filterItemsByStatus(String status){ 115 | ArrayList selected_items = new ArrayList<>(); 116 | for (Item i: items) { 117 | if (i.getStatus().equals(status)) { 118 | selected_items.add(i); 119 | } 120 | } 121 | return selected_items; 122 | } 123 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ItemListController.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * ItemListController is responsible for all communication between views and ItemList object 9 | */ 10 | public class ItemListController { 11 | 12 | private ItemList item_list; 13 | 14 | public ItemListController(ItemList item_list){ 15 | this.item_list = item_list; 16 | } 17 | 18 | public void setItems(ArrayList item_list) { 19 | this.item_list.setItems(item_list); 20 | } 21 | 22 | public ArrayList getItems() { 23 | return item_list.getItems(); 24 | } 25 | 26 | public boolean addItem(Item item, Context context){ 27 | AddItemCommand add_item_command = new AddItemCommand(item_list, item, context); 28 | add_item_command.execute(); 29 | return add_item_command.isExecuted(); 30 | } 31 | 32 | public boolean deleteItem(Item item, Context context) { 33 | DeleteItemCommand delete_item_command = new DeleteItemCommand(item_list, item, context); 34 | delete_item_command.execute(); 35 | return delete_item_command.isExecuted(); 36 | } 37 | 38 | public boolean editItem(Item item, Item updated_item, Context context){ 39 | EditItemCommand edit_item_command = new EditItemCommand(item_list, item, updated_item, context); 40 | edit_item_command.execute(); 41 | return edit_item_command.isExecuted(); 42 | } 43 | 44 | public Item getItem(int index) { 45 | return item_list.getItem(index); 46 | } 47 | 48 | public int getIndex(Item item) { 49 | return item_list.getIndex(item); 50 | } 51 | 52 | public int getSize() { 53 | return item_list.getSize(); 54 | } 55 | 56 | public void loadItems(Context context) { 57 | item_list.loadItems(context); 58 | } 59 | 60 | public ArrayList getActiveBorrowers() { 61 | return item_list.getActiveBorrowers(); 62 | } 63 | 64 | public ArrayList filterItemsByStatus(String status){ 65 | return item_list.filterItemsByStatus(status); 66 | } 67 | 68 | public void addObserver(Observer observer) { 69 | item_list.addObserver(observer); 70 | } 71 | 72 | public void removeObserver(Observer observer) { 73 | item_list.removeObserver(observer); 74 | } 75 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/ItemsFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.AdapterView; 11 | import android.widget.ArrayAdapter; 12 | import android.widget.ListView; 13 | 14 | import java.util.ArrayList; 15 | 16 | /** 17 | * Superclass of AvailableItemsFragment, BorrowedItemsFragment and AllItemsFragment 18 | */ 19 | public abstract class ItemsFragment extends Fragment implements Observer { 20 | 21 | private ItemList item_list = new ItemList(); 22 | ItemListController item_list_controller = new ItemListController(item_list); 23 | 24 | View rootView; 25 | private ListView list_view; 26 | private ArrayAdapter adapter; 27 | private ArrayList selected_items; 28 | private LayoutInflater inflater; 29 | private ViewGroup container; 30 | private Context context; 31 | private Fragment fragment; 32 | private boolean update = false; 33 | 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | context = getContext(); 37 | 38 | // Don't update view yet. Wait until after items have been filtered. 39 | item_list_controller.loadItems(context); 40 | update = true; 41 | 42 | this.inflater = inflater; 43 | this.container = container; 44 | 45 | return rootView; 46 | } 47 | 48 | public void setVariables(int resource, int id ) { 49 | rootView = inflater.inflate(resource, container, false); 50 | list_view = (ListView) rootView.findViewById(id); 51 | selected_items = filterItems(); 52 | } 53 | 54 | public void loadItems(Fragment fragment){ 55 | this.fragment = fragment; 56 | item_list_controller.addObserver(this); 57 | item_list_controller.loadItems(context); 58 | } 59 | 60 | public void setFragmentOnItemLongClickListener(){ 61 | 62 | // When item is long clicked, this starts EditItemActivity 63 | list_view.setOnItemLongClickListener(new android.widget.AdapterView.OnItemLongClickListener() { 64 | 65 | @Override 66 | public boolean onItemLongClick(AdapterView parent, View view, int pos, long id) { 67 | 68 | Item item = adapter.getItem(pos); 69 | 70 | int meta_pos = item_list_controller.getIndex(item); 71 | if (meta_pos >= 0) { 72 | 73 | Intent edit = new Intent(context, EditItemActivity.class); 74 | edit.putExtra("position", meta_pos); 75 | startActivity(edit); 76 | } 77 | return true; 78 | } 79 | }); 80 | } 81 | 82 | /** 83 | * filterItems is implemented independently by AvailableItemsFragment, BorrowedItemsFragment and AllItemsFragment 84 | * @return selected_items 85 | */ 86 | public abstract ArrayList filterItems(); 87 | 88 | /** 89 | * Called when the activity is destroyed, thus we remove this fragment as an observer 90 | */ 91 | @Override 92 | public void onDestroy() { 93 | super.onDestroy(); 94 | item_list_controller.removeObserver(this); 95 | } 96 | 97 | /** 98 | * Update the view 99 | */ 100 | @Override 101 | public void update(){ 102 | if (update) { 103 | adapter = new ItemAdapter(context, selected_items, fragment); 104 | list_view.setAdapter(adapter); 105 | adapter.notifyDataSetChanged(); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.content.Intent; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | 8 | import android.support.v4.view.ViewPager; 9 | import android.os.Bundle; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | 14 | /** 15 | * Home Activity of the App 16 | */ 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | 24 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 25 | setSupportActionBar(toolbar); 26 | // Create the adapter that will return a fragment for each of the three 27 | // primary sections of the activity. 28 | SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); 29 | 30 | // Set up the ViewPager with the sections adapter. 31 | ViewPager mViewPager = (ViewPager) findViewById(R.id.container); 32 | mViewPager.setAdapter(mSectionsPagerAdapter); 33 | mViewPager.setOffscreenPageLimit(0); 34 | 35 | TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); 36 | tabLayout.setupWithViewPager(mViewPager); 37 | } 38 | 39 | @Override 40 | public boolean onCreateOptionsMenu(Menu menu) { 41 | // Inflate the menu; this adds items to the action bar if it is present. 42 | getMenuInflater().inflate(R.menu.menu_main, menu);//Menu Resource, Menu 43 | return true; 44 | } 45 | 46 | @Override 47 | public boolean onOptionsItemSelected(MenuItem item) { 48 | switch (item.getItemId()) { 49 | case R.id.contacts: 50 | Intent intent = new Intent(this, ContactsActivity.class); 51 | startActivity(intent); 52 | return true; 53 | default: 54 | return super.onOptionsItemSelected(item); 55 | } 56 | } 57 | 58 | public void addItemActivity(View view) { 59 | Intent intent = new Intent(this, AddItemActivity.class); 60 | startActivity(intent); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/Observable.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Superclass of Item, ItemList, Contact, ContactList 7 | */ 8 | public class Observable { 9 | 10 | private ArrayList observers = null; 11 | 12 | public Observable(){ 13 | observers = new ArrayList(); 14 | } 15 | 16 | // Notify observers when need to update any changes made to model 17 | public void notifyObservers() { 18 | for (Observer observer : observers) { 19 | observer.update(); 20 | } 21 | } 22 | 23 | public void addObserver(Observer observer) { 24 | observers.add(observer); 25 | } 26 | 27 | public void removeObserver(Observer observer) { 28 | if (observers.contains(observer)) { 29 | observers.remove(observer); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/Observer.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | /** 4 | * Observer Interface 5 | */ 6 | public interface Observer { 7 | public void update(); 8 | } 9 | -------------------------------------------------------------------------------- /SharingApp/app/src/main/java/com/example/sharingapp/SectionsPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.sharingapp; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | 7 | /** 8 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to 9 | * one of the sections/tabs/pages. 10 | */ 11 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 12 | 13 | public SectionsPagerAdapter(FragmentManager fm) { 14 | super(fm); 15 | } 16 | 17 | @Override 18 | public Fragment getItem(int position) { 19 | switch (position) { 20 | case 0: 21 | AllItemsFragment all_items_fragment = new AllItemsFragment(); 22 | return all_items_fragment; 23 | case 1: 24 | AvailableItemsFragment available_items_fragment = new AvailableItemsFragment(); 25 | return available_items_fragment; 26 | case 2: 27 | BorrowedItemsFragment borrowed_items_fragment = new BorrowedItemsFragment(); 28 | return borrowed_items_fragment; 29 | default: 30 | return null; 31 | } 32 | } 33 | 34 | @Override 35 | public int getCount() { 36 | return 3; // Three pages 37 | } 38 | 39 | @Override 40 | public CharSequence getPageTitle(int position) { 41 | switch (position) { 42 | case 0: 43 | return "All"; 44 | case 1: 45 | return "Available"; 46 | case 2: 47 | return "Borrowed"; 48 | } 49 | return null; 50 | } 51 | } -------------------------------------------------------------------------------- /SharingApp/app/src/main/res/layout/activity_add_contact.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 19 | 20 | 27 | 28 | 36 | 37 | 38 | 39 | 45 | 46 | 53 | 54 | 62 | 63 | 64 | 65 | 71 | 72 |