diff --git a/app/build.gradle b/app/build.gradle index b8a8cf2..324d086 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,11 +24,16 @@ android { } } +repositories { + mavenCentral() +} + dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' + compile 'com.google.code.gson:gson:2.6.2' compile 'com.squareup.retrofit2:retrofit:2.0.1' compile 'com.squareup.retrofit2:converter-gson:2.0.1' @@ -38,4 +43,5 @@ dependencies { compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.android.support:design:23.3.0' compile 'com.android.support:cardview-v7:23.3.0' + compile 'com.facebook.android:facebook-android-sdk:4.11.0' } diff --git a/app/libs/socialauth-4.4.jar b/app/libs/socialauth-4.4.jar new file mode 100644 index 0000000..ed8ecf6 Binary files /dev/null and b/app/libs/socialauth-4.4.jar differ diff --git a/app/libs/socialauth-android-3.2.jar b/app/libs/socialauth-android-3.2.jar new file mode 100644 index 0000000..04cc9d3 Binary files /dev/null and b/app/libs/socialauth-android-3.2.jar differ diff --git a/app/src/androidTest/java/hikapro/com/backpack/ApplicationTest.java b/app/src/androidTest/java/com/hikapro/backpack/ApplicationTest.java similarity index 91% rename from app/src/androidTest/java/hikapro/com/backpack/ApplicationTest.java rename to app/src/androidTest/java/com/hikapro/backpack/ApplicationTest.java index 50b1479..ee17456 100644 --- a/app/src/androidTest/java/hikapro/com/backpack/ApplicationTest.java +++ b/app/src/androidTest/java/com/hikapro/backpack/ApplicationTest.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack; +package com.hikapro.backpack; import android.app.Application; import android.test.ApplicationTestCase; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index df33a52..dc7c65f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,23 +1,33 @@ + package="com.hikapro.backpack"> + + - + + + + + \ No newline at end of file diff --git a/app/src/main/assets/fonts/Ubuntu-B.ttf b/app/src/main/assets/fonts/Ubuntu-B.ttf new file mode 100644 index 0000000..c0142fe Binary files /dev/null and b/app/src/main/assets/fonts/Ubuntu-B.ttf differ diff --git a/app/src/main/assets/fonts/Ubuntu-M.ttf b/app/src/main/assets/fonts/Ubuntu-M.ttf new file mode 100644 index 0000000..4a776d3 Binary files /dev/null and b/app/src/main/assets/fonts/Ubuntu-M.ttf differ diff --git a/app/src/main/assets/jsons/item_categories.json b/app/src/main/assets/jsons/item_categories.json new file mode 100644 index 0000000..422ad05 --- /dev/null +++ b/app/src/main/assets/jsons/item_categories.json @@ -0,0 +1 @@ +[{"id":16,"name":"Bug Sprays"},{"id":1,"name":"Clothing"},{"id":8,"name":"Documents"},{"id":11,"name":"Electronics"},{"id":3,"name":"Equipment"},{"id":14,"name":"Furniture"},{"id":7,"name":"Groceries"},{"id":4,"name":"Medicines"},{"id":5,"name":"Personal Care"},{"id":15,"name":"Tools"},{"id":2,"name":"Utensils"}] \ No newline at end of file diff --git a/app/src/main/assets/jsons/items.json b/app/src/main/assets/jsons/items.json new file mode 100644 index 0000000..1f44734 --- /dev/null +++ b/app/src/main/assets/jsons/items.json @@ -0,0 +1 @@ +[{"id":384,"item_category_id":3,"name":"Accessory cord","description":"Useful when tying down your camp gear or setting up a belay anchor.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=accessory%20cord\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/384/medium/AccessoryCord.jpg?1449412546","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/384/thumb/AccessoryCord.jpg?1449412546"},{"id":389,"item_category_id":3,"name":"Acetylene","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/389/medium/Acetylene.jpg?1451147832","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/389/thumb/Acetylene.jpg?1451147832"},{"id":390,"item_category_id":3,"name":"Acetylene gas lamp","description":"","buy_urls":["http://google.com"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/390/medium/AcetyleneGasLamp.jpg?1451147857","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/390/thumb/AcetyleneGasLamp.jpg?1451147857"},{"id":290,"item_category_id":4,"name":"Analeptic","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Analeptic"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/290/medium/Antibacterials.jpg?1452023609","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/290/thumb/Antibacterials.jpg?1452023609"},{"id":266,"item_category_id":4,"name":"Antibacterials","description":"Advise your doctor to choose the medicine.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/266/medium/Antibacterials.jpg?1451145119","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/266/thumb/Antibacterials.jpg?1451145119"},{"id":265,"item_category_id":4,"name":"Anti-diarrhoeal / activated charcoal","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Antidiarrhoeal"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/265/medium/Antibacterials.jpg?1452019909","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/265/thumb/Antibacterials.jpg?1452019909"},{"id":284,"item_category_id":4,"name":"Antiemetic","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Antiemetic"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/284/medium/Antibacterials.jpg?1452020084","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/284/thumb/Antibacterials.jpg?1452020084"},{"id":268,"item_category_id":4,"name":"Antihistamine","description":"Itching, sneezing, and inflammatory responses are suppressed by antihistamines. Advise your doctor to choose the medicine.","buy_urls":["https://en.wikipedia.org/wiki/Antihistamine"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/268/medium/Antihistamine.jpg?1451146696","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/268/thumb/Antihistamine.jpg?1451146696"},{"id":269,"item_category_id":4,"name":"Antiseptic","description":"Antiseptics are antimicrobial substances that are applied to living tissue/skin to reduce the possibility of infection, sepsis, or putrefaction.","buy_urls":["https://en.wikipedia.org/wiki/Antiseptic"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/269/medium/Antiseptic.jpg?1451146991","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/269/thumb/Antiseptic.jpg?1451146991"},{"id":289,"item_category_id":4,"name":"Antispasmodic ","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/289/medium/Antibacterials.jpg?1452021171","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/289/thumb/Antibacterials.jpg?1452021171"},{"id":365,"item_category_id":3,"name":"Backpack","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=backpack\u0026s=u"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/365/medium/BackPack.jpg?1449412856","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/365/thumb/BackPack.jpg?1449412856"},{"id":428,"item_category_id":3,"name":"Backpack 20L","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=backpacks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/428/medium/Backpack20.jpg?1452075315","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/428/thumb/Backpack20.jpg?1452075315"},{"id":360,"item_category_id":3,"name":"Backpack cover","description":"Light backpack raincover will protect it from getting wet, will save your clothes dry and light.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=backpack%20covers\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/360/medium/BackpackCover.jpg?1449412930","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/360/thumb/BackpackCover.jpg?1449412930"},{"id":424,"item_category_id":3,"name":"Back protection","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=back%20protection\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/424/medium/BackProtection.jpg?1452074444","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/424/thumb/BackProtection.jpg?1452074444"},{"id":295,"item_category_id":1,"name":"Balaclava","description":"A balaclava is a form of cloth headgear designed to expose only part of the face. Depending on style and how it is worn, only the eyes, mouth and nose, or just the front of the face are unprotected. Versions with a full face opening may be rolled into a hat to cover the crown of the head or folded down as a collar around the neck.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=balaclava\u0026s=u"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/295/medium/Balaclava.jpg?1449413002","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/295/thumb/Balaclava.jpg?1449413002"},{"id":395,"item_category_id":11,"name":"Batteries","description":"Take enough batteries for all your electric devices: GPS, headlamp, camera and others.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=batteries\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/395/medium/Batteries.jpg?1449413069","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/395/thumb/Batteries.jpg?1449413069"},{"id":314,"item_category_id":1,"name":"Boots","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hiking%20boots\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/314/medium/Boots.jpg?1449413274","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/314/thumb/Boots.jpg?1449413274"},{"id":267,"item_category_id":4,"name":"Broad spectrum antibiotic","description":"Antibiotic that acts against a wide range of disease-causing bacteria. Advise your doctor to choose the medicine.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/267/medium/BroadSpectrum_Antibiotics.jpg?1449413455","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/267/thumb/BroadSpectrum_Antibiotics.jpg?1449413455"},{"id":249,"item_category_id":15,"name":"Bucket","description":"May be used to bring water, wash dishes, fruits or vegetables, gather berries or mushrooms.","buy_urls":["http://www.backcountry.com/sea-to-summit-folding-bucket"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/249/medium/Bucket.jpg?1449413359","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/249/thumb/Bucket.jpg?1449413359"},{"id":296,"item_category_id":1,"name":"Buff","description":"May be used as a hat, scarf, bandana, head or wrist band.","buy_urls":["http://www.backcountry.com/buff-uv-bug-slinger-buff-water-camo-print"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/296/medium/Buff.jpg?1449413472","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/296/thumb/Buff.jpg?1449413472"},{"id":282,"item_category_id":4,"name":"Burn gel or spray","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/282/medium/BurnGel.jpg?1452019290","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/282/thumb/BurnGel.jpg?1452019290"},{"id":370,"item_category_id":3,"name":"Camping canopy","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/370/medium/Canopy.jpeg?1453300410","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/370/thumb/Canopy.jpeg?1453300410"},{"id":352,"item_category_id":3,"name":"Camping stove","description":"Necessary if you plan to cook or boil water on gas.","buy_urls":["http://www.backcountry.com/snow-peak-bipod-stove"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/352/medium/CampingStove.jpg?1449413664","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/352/thumb/CampingStove.jpg?1449413664"},{"id":312,"item_category_id":1,"name":"Camp shoes","description":"Spare shoes to let your feet relax","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hiking%20sandals\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/312/medium/CampShoes.jpg?1449417075","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/312/thumb/CampShoes.jpg?1449417075"},{"id":366,"item_category_id":3,"name":"Candle","description":"Be careful with fire.\r\nA single candle may heat the air inside the tent to 5C.","buy_urls":["https://en.wikipedia.org/wiki/Candle"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/366/medium/Candle.jpg?1452020883","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/366/thumb/Candle.jpg?1452020883"},{"id":307,"item_category_id":1,"name":"Cap","description":"Cap, hat or alternative is essential in every trip to protect you head and face from sun, rain and wind.","buy_urls":["http://www.backcountry.com/arcteryx-big-a-cap"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/307/medium/Cap.jpg?1449414270","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/307/thumb/Cap.jpg?1449414270"},{"id":287,"item_category_id":4,"name":"Cardiovascular medication","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/287/medium/Antibacterials.jpg?1452020962","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/287/thumb/Antibacterials.jpg?1452020962"},{"id":235,"item_category_id":8,"name":"Cash and bank cards","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/235/medium/CashAndCards.jpg?1451154423","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/235/thumb/CashAndCards.jpg?1451154423"},{"id":294,"item_category_id":14,"name":"Chairs","description":"","buy_urls":["http://www.backcountry.com/kelty-deluxe-lounge-chair"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/294/medium/Chair.jpg?1449414319","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/294/thumb/Chair.jpg?1449414319"},{"id":358,"item_category_id":3,"name":"Chalk","description":"Keeps your hands dry while climbing.","buy_urls":["http://www.backcountry.com/petzl-power-crunch-chalk"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/358/medium/Chalk.jpg?1449414362","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/358/thumb/Chalk.jpg?1449414362"},{"id":377,"item_category_id":3,"name":"Climbing rope","description":"Special rope with specific dinamyc and durability characteristics. Have it with you when travelling up or down.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=climbing%20ropes\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/377/medium/ClimbingRope.jpg?1449414446","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/377/thumb/ClimbingRope.jpg?1449414446"},{"id":385,"item_category_id":3,"name":"Climbing shoes","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=climbing%20shoes\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/385/medium/ClimbingShoes.jpg?1449414896","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/385/thumb/ClimbingShoes.jpg?1449414896"},{"id":225,"item_category_id":5,"name":"Comb","description":"","buy_urls":["https://en.wikipedia.org/wiki/Comb"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/225/medium/Comb.jpg?1452020221","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/225/thumb/Comb.jpg?1452020221"},{"id":356,"item_category_id":3,"name":"Compass","description":"Will always help you orient, get back to the camp or continue floowing the route.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=compass\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/356/medium/Compass.jpg?1449414960","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/356/thumb/Compass.jpg?1449414960"},{"id":213,"item_category_id":5,"name":"Cotton swabs","description":"Are commonly used in a variety of applications including first aid, cosmetics application, cleaning.","buy_urls":["https://en.wikipedia.org/wiki/Cotton_swab"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/213/medium/CottonSwabs.jpg?1451152942","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/213/thumb/CottonSwabs.jpg?1451152942"},{"id":272,"item_category_id":4,"name":"Cotton wool","description":"Absorbent Cotton is also known as Surgical Cotton or Cotton Wool and mainly used\r\nfor medical purposes in hospitals, nursing homes, dispensaries and at home (for first\r\naid) etc. because of its property of high fluid absorbency, it is better known among\r\nmasses as absorbent cotton.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/272/medium/CottonWool.jpg?1451152882","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/272/thumb/CottonWool.jpg?1451152882"},{"id":279,"item_category_id":4,"name":"Cough medicine","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Cough_medicine"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/279/medium/Antihistamine.jpg?1452020042","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/279/thumb/Antihistamine.jpg?1452020042"},{"id":381,"item_category_id":3,"name":"Crampons","description":"Essential in alpine trips.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=crampons\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/381/medium/Crampons.jpg?1449415018","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/381/thumb/Crampons.jpg?1449415018"},{"id":343,"item_category_id":2,"name":"Cutting board","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=cutting%20boards\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/343/medium/CuttingBoard.jpg?1449415076","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/343/thumb/CuttingBoard.jpg?1449415076"},{"id":409,"item_category_id":5,"name":"Deodorant","description":"Will keep your body fresh during the trip.","buy_urls":["https://en.wikipedia.org/wiki/Deodorant"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/409/medium/Deodorant.jpg?1451153290","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/409/thumb/Deodorant.jpg?1451153290"},{"id":406,"item_category_id":2,"name":"Disposable tableware","description":"Useful for picknick or car trip.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=disposable\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/406/medium/DisposableTablewear.jpg?1449415151","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/406/thumb/DisposableTablewear.jpg?1449415151"},{"id":243,"item_category_id":15,"name":"Distilled water","description":"Can be used in car`s cooling and windshield cleaning systems, for washing and drinking.","buy_urls":["https://en.wikipedia.org/wiki/Distilled_water"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/243/medium/DistilledWater.jpg?1451153338","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/243/thumb/DistilledWater.jpg?1451153338"},{"id":271,"item_category_id":4,"name":"Dressings and bandages","description":"A bandage is a piece of material used either to support a medical device such as a dressing or splint, or on its own to provide support to or to restrict the movement of a part of the body. When used with a dressing, the dressing is applied directly on a wound, and a bandage used to hold the dressing in place.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=first%20aid%20kit%20backpacking\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/271/medium/DressingsBandages.jpg?1449415262","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/271/thumb/DressingsBandages.jpg?1449415262"},{"id":233,"item_category_id":8,"name":"Driver's license","description":"Never leave your home without a piece of ID.","buy_urls":["https://en.wikipedia.org/wiki/Driver%27s_license"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/233/medium/DriversLicense.jpg?1451153080","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/233/thumb/DriversLicense.jpg?1451153080"},{"id":292,"item_category_id":4,"name":"Elastic bandage","description":"Elastic bandages are commonly used to treat muscle sprains and strains by reducing the flow of blood to a particular area by the application of even stable pressure which can restrict swelling at the place of injury. Elastic bandages are also used to treat bone fractures.","buy_urls":["https://en.wikipedia.org/wiki/Elastic_bandage"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/292/medium/ElasticBandage.jpg?1452023805","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/292/thumb/ElasticBandage.jpg?1452023805"},{"id":239,"item_category_id":8,"name":"Emergency phone numbers","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/239/medium/Emergency.jpeg?1453300246","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/239/thumb/Emergency.jpeg?1453300246"},{"id":401,"item_category_id":11,"name":"Extension cord","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/401/medium/ExtensionCord.jpeg?1453300886","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/401/thumb/ExtensionCord.jpeg?1453300886"},{"id":391,"item_category_id":11,"name":"Extra lamp","description":"Essential in trips, where a lamp is used more often, where a working lamp at hand is a requirement, while caving, for example.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=headlamps\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/391/medium/HeadLamp.jpg?1451153542","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/391/thumb/HeadLamp.jpg?1451153542"},{"id":316,"item_category_id":1,"name":"Extra outwear","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=outerwear\u0026s=a"],"photo":"","photo_thumb":""},{"id":311,"item_category_id":1,"name":"Extra pair of socks","description":"It is important to have enough of comfortable socks, suitable for youe type of travel.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=socks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/311/medium/ExtraSocks.jpg?1449415489","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/311/thumb/ExtraSocks.jpg?1449415489"},{"id":319,"item_category_id":1,"name":"Extra sun glasses","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sunglass\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/319/medium/ExtraSunglasses.jpg?1449415560","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/319/thumb/ExtraSunglasses.jpg?1449415560"},{"id":317,"item_category_id":1,"name":"Extra underwear","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=underwear\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/317/medium/ExtraUnderwear.jpg?1449415670","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/317/thumb/ExtraUnderwear.jpg?1449415670"},{"id":257,"item_category_id":15,"name":"Extra work gloves","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=work%20gloves\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/257/medium/ExtraWorkGloves.jpg?1449415740","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/257/thumb/ExtraWorkGloves.jpg?1449415740"},{"id":273,"item_category_id":4,"name":"Eye drops","description":"Eye drops sometimes do not have medications in them and are only lubricating and tear-replacing solutions. Advise with your doctor to get the right medicine.","buy_urls":["https://en.wikipedia.org/wiki/Eye_drop"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/273/medium/EyeDrops.jpg?1451153216","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/273/thumb/EyeDrops.jpg?1451153216"},{"id":228,"item_category_id":5,"name":"Feminine hygiene","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/228/medium/FeminineHygiene.jpg?1452021270","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/228/thumb/FeminineHygiene.jpg?1452021270"},{"id":274,"item_category_id":4,"name":"Fever reducer","description":"Always advise your doctor to chose the right medicine.","buy_urls":["https://en.wikipedia.org/wiki/Antipyretic"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/274/medium/FeverReducer.jpg?1451153660","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/274/thumb/FeverReducer.jpg?1451153660"},{"id":369,"item_category_id":3,"name":"Fire starting kit","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=fire%20starter%20kits\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/369/medium/FireStartingKit.jpg?1449415824","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/369/thumb/FireStartingKit.jpg?1449415824"},{"id":270,"item_category_id":4,"name":"First-aid kit","description":"First-aid kit is essential in each and every outdoor trip.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=first%20aid%20kits\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/270/medium/FirstAidKit.jpg?1451147291","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/270/thumb/FirstAidKit.jpg?1451147291"},{"id":407,"item_category_id":11,"name":"Flash card","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/407/medium/FlashCard.jpeg?1453301073","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/407/thumb/FlashCard.jpeg?1453301073"},{"id":331,"item_category_id":7,"name":"Food and drinks","description":"Always carry enough food and drinks when leaving a camp.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=foods\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/331/medium/Food.jpg?1449415994","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/331/thumb/Food.jpg?1449415994"},{"id":334,"item_category_id":2,"name":"Food container","description":"Will keep food odors inside the container in order not to attract wild animals like bears to your camp, will keep your food safe from rodents.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=food%20containers\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/334/medium/FoodCanister.jpg?1449416066","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/334/thumb/FoodCanister.jpg?1449416066"},{"id":333,"item_category_id":2,"name":"Fork","description":"Better use spork.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sporks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/333/medium/Fork.jpg?1449416141","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/333/thumb/Fork.jpg?1449416141"},{"id":298,"item_category_id":1,"name":"Gaiters","description":"Gaiters protect your boots from snow getting in at winter trips, also keep you boots safe from sand, dust and insects during summer hikes.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=gaiters\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/298/medium/Gaiters.jpg?1449416201","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/298/thumb/Gaiters.jpg?1449416201"},{"id":349,"item_category_id":3,"name":"Gas","description":"Make sure that you take enough gas for the trip, try to envisage supply options.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=gas%20stoves\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/349/medium/Gas.jpg?1449416339","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/349/thumb/Gas.jpg?1449416339"},{"id":422,"item_category_id":3,"name":"Goggles","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20goggles\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/422/medium/Goggles.jpg?1452074226","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/422/thumb/Goggles.jpg?1452074226"},{"id":394,"item_category_id":11,"name":"GPS device","description":"Equipped with actual maps will always help you find the right route, way back to the camp or home. Have enough batteries ready for it.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=gps\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/394/medium/GPS.jpg?1449416384","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/394/thumb/GPS.jpg?1449416384"},{"id":393,"item_category_id":11,"name":"Hairdryer","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/393/medium/HairDryer.jpeg?1453300939","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/393/thumb/HairDryer.jpeg?1453300939"},{"id":251,"item_category_id":15,"name":"Hammer","description":"Useful in repairing and setting up a camp.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hammers\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/251/medium/Hammer.jpg?1449416493","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/251/thumb/Hammer.jpg?1449416493"},{"id":223,"item_category_id":5,"name":"Hand sanitizer","description":"A hand antiseptic, hand disinfectant, hand sanitiser or hand sanitizer is a supplement or alternative to hand washing with soap and water.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sanitizer\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/223/medium/HandSanitizer.jpg?1449416442","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/223/thumb/HandSanitizer.jpg?1449416442"},{"id":387,"item_category_id":3,"name":"Harness","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=harness\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/387/medium/Harness.jpg?1449416542","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/387/thumb/Harness.jpg?1449416542"},{"id":299,"item_category_id":1,"name":"Hat","description":"Hat or alternative is essential in every trip to protect you head and face from sun, rain and wind.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hat\u0026s=u"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/299/medium/Hat.jpg?1449416618","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/299/thumb/Hat.jpg?1449416618"},{"id":371,"item_category_id":3,"name":"Haul bag","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=haul%20bags\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/371/medium/HaulBag.jpg?1449416663","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/371/thumb/HaulBag.jpg?1449416663"},{"id":397,"item_category_id":11,"name":"Head lamp","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=headlamps\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/397/medium/HeadLamp.jpg?1449416712","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/397/thumb/HeadLamp.jpg?1449416712"},{"id":405,"item_category_id":11,"name":"Headphones","description":"Travelling with your favourite music is easier.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=head%20phones\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/405/medium/HeadPhones.jpg?1449416765","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/405/thumb/HeadPhones.jpg?1449416765"},{"id":324,"item_category_id":1,"name":"Heavyweight thermal underwear","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=thermal%20underwear\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/324/medium/HeavyThermalUnderwear.jpg?1449416822","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/324/thumb/HeavyThermalUnderwear.jpg?1449416822"},{"id":380,"item_category_id":3,"name":"Helmet","description":"Protects your head from rocks and ice during mountain trips.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=helmets\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/380/medium/Helmet.jpg?1449416870","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/380/thumb/Helmet.jpg?1449416870"},{"id":325,"item_category_id":1,"name":"Hiking boots","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hiking%20boots\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/325/medium/HikingBoots.jpg?1449416971","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/325/thumb/HikingBoots.jpg?1449416971"},{"id":347,"item_category_id":3,"name":"Hiking sauna","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/347/medium/HikingSauna.jpg?1451151417","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/347/thumb/HikingSauna.jpg?1451151417"},{"id":326,"item_category_id":1,"name":"Hiking socks","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hiking%20socks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/326/medium/HikingSocks.jpg?1449417121","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/326/thumb/HikingSocks.jpg?1449417121"},{"id":392,"item_category_id":3,"name":"Human waste bags","description":"Useful in caving and other trips in order to take away human wastes and keep the nature clean.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=waste%20bags\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/392/medium/HumanWasteBags.jpg?1449417163","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/392/thumb/HumanWasteBags.jpg?1449417163"},{"id":351,"item_category_id":3,"name":"Hydration pack","description":"Allows to take more drinking water that regular bottle, to drink on the go without unnecessary stops and fear to drop or lose the bottle.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hydration%20packs\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/351/medium/HydrationPack.jpg?1449417208","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/351/thumb/HydrationPack.jpg?1449417208"},{"id":382,"item_category_id":3,"name":"Ice axe","description":"Choose by weight and usage comfortability.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ice%20axes\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/382/medium/IceAxe.jpg?1449417256","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/382/thumb/IceAxe.jpg?1449417256"},{"id":275,"item_category_id":4,"name":"Individual medicines","description":"Advise your doctor to pack an individual first-aid kit with all personal medicines for all your chronic illnesses.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=medical%20kits\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/275/medium/IndividMedicine.jpg?1449417320","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/275/thumb/IndividMedicine.jpg?1449417320"},{"id":346,"item_category_id":16,"name":"Insects repellent","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=repellents\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/346/medium/InsectsRepell.jpg?1449417399","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/346/thumb/InsectsRepell.jpg?1449417399"},{"id":263,"item_category_id":15,"name":"Insulating tape","description":"Useg as a fixing tool in caravaning and hiking trips.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=tape\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/263/medium/InsulatingTape.jpg?1449417477","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/263/thumb/InsulatingTape.jpg?1449417477"},{"id":238,"item_category_id":8,"name":"Insurance policy","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/238/medium/insurance.jpeg?1453300115","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/238/thumb/insurance.jpeg?1453300115"},{"id":277,"item_category_id":4,"name":"Intestinal infection medication","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/277/medium/BroadSpectrum_Antibiotics.jpg?1452023163","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/277/thumb/BroadSpectrum_Antibiotics.jpg?1452023163"},{"id":375,"item_category_id":3,"name":"Kayak","description":"You may find a suitable tree and hollow out your own kayak, but better take a modern one and save the nature.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=kayaks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/375/medium/Kayak.jpg?1449417666","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/375/thumb/Kayak.jpg?1449417666"},{"id":341,"item_category_id":2,"name":"Kettle","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=kettle%20pot\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/341/medium/Kettle.jpg?1449417740","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/341/thumb/Kettle.jpg?1449417740"},{"id":361,"item_category_id":3,"name":"Knee pads","description":"Support the knees during walking and protect during setting up a camp.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=knee%20pads\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/361/medium/KneePads.jpg?1449417799","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/361/thumb/KneePads.jpg?1449417799"},{"id":338,"item_category_id":2,"name":"Knife","description":"Every tourist needs a knife in his backpack.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=knife\u0026s=u"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/338/medium/Knife.jpg?1449417846","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/338/thumb/Knife.jpg?1449417846"},{"id":342,"item_category_id":2,"name":"Ladle","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ladle\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/342/medium/Ladle.jpg?1449417925","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/342/thumb/Ladle.jpg?1449417925"},{"id":260,"item_category_id":15,"name":"Lamp repair kit","description":"","buy_urls":[],"photo":"","photo_thumb":""},{"id":221,"item_category_id":5,"name":"Laundry soap","description":"Travelling in fresh and clean clothes is easier and more enjoyable.","buy_urls":["https://en.wikipedia.org/wiki/Laundry_detergent"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/221/medium/LaundrySoap.jpg?1451154375","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/221/thumb/LaundrySoap.jpg?1451154375"},{"id":288,"item_category_id":4,"name":"Laxative","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/288/medium/Antibacterials.jpg?1452021127","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/288/thumb/Antibacterials.jpg?1452021127"},{"id":376,"item_category_id":3,"name":"Life vest","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=life%20vest\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/376/medium/LifeVest.jpg?1449418037","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/376/thumb/LifeVest.jpg?1449418037"},{"id":244,"item_category_id":15,"name":"Lifting jack","description":"Can be used for wheel changing or while freeing a stuck vehicle.","buy_urls":["https://en.wikipedia.org/wiki/Jack_(device)"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/244/medium/LiftingJack.jpg?1451153476","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/244/thumb/LiftingJack.jpg?1451153476"},{"id":396,"item_category_id":11,"name":"Light bulb","description":"We recommend having an extra light bulb during the trips with active lamp usage.","buy_urls":["https://en.wikipedia.org/wiki/Lamp_(electrical_component)"],"photo":"","photo_thumb":""},{"id":306,"item_category_id":1,"name":"Light fleece hoodie","description":"Fleece outwear saves your warmth and removes moisture better.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=hoodies\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/306/medium/LightFleeceHoodie.jpg?1449418151","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/306/thumb/LightFleeceHoodie.jpg?1449418151"},{"id":305,"item_category_id":1,"name":"Light jacket","description":"Take into account weather and temperature conditions while preparing and selecting outerwear. Light jacket may come in handy even during summer time hikes.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=light%20jackets\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/305/medium/LiteJacket.jpg?1449418214","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/305/thumb/LiteJacket.jpg?1449418214"},{"id":315,"item_category_id":1,"name":"Lightweight thermal underwear","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=lightweight%20underwear\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/315/medium/LiteThermalUnder.jpg?1449418284","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/315/thumb/LiteThermalUnder.jpg?1449418284"},{"id":215,"item_category_id":5,"name":"Lip balm","description":"Protects lips from chapping in any trip.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=lip%20balms\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/215/medium/LipBalm.jpg?1449418345","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/215/thumb/LipBalm.jpg?1449418345"},{"id":241,"item_category_id":15,"name":"Lug wrench","description":"Be sure to come in handy if you need to replace the wheel. A type of socket wrench used to loosen and tighten lug nuts on automobile wheels.","buy_urls":["https://en.wikipedia.org/wiki/Lug_wrench"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/241/medium/LugWrench.jpg?1451150607","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/241/thumb/LugWrench.jpg?1451150607"},{"id":354,"item_category_id":3,"name":"Map","description":"We recommend to have a paper map in waterproof cover, even if you travel with GPS device.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=maps\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/354/medium/Map.jpg?1449418415","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/354/thumb/Map.jpg?1449418415"},{"id":258,"item_category_id":15,"name":"Mat repair kit","description":"","buy_urls":["http://www.backcountry.com/sleeping-pad-accessories"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/258/medium/MatRepairKit.jpg?1449418479","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/258/thumb/MatRepairKit.jpg?1449418479"},{"id":335,"item_category_id":2,"name":"Metal pot","description":"Choose a pot depending on amount of people you are going to cook for.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=pots\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/335/medium/MetalPot.jpg?1449418527","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/335/thumb/MetalPot.jpg?1449418527"},{"id":323,"item_category_id":1,"name":"Midweight thermal underwear","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=midweight%20thermal%20underwear\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/323/medium/MidweightThermal.jpg?1449418585","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/323/thumb/MidweightThermal.jpg?1449418585"},{"id":313,"item_category_id":1,"name":"Mittens","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=mittens\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/313/medium/Mittens.jpg?1449418807","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/313/thumb/Mittens.jpg?1449418807"},{"id":400,"item_category_id":11,"name":"Mobile phone","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/400/medium/Phone.jpeg?1453300179","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/400/thumb/Phone.jpeg?1453300179"},{"id":345,"item_category_id":16,"name":"Mosquito repellent","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=repellents\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/345/medium/InsectsRepell.jpg?1449418862","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/345/thumb/InsectsRepell.jpg?1449418862"},{"id":245,"item_category_id":15,"name":"Motor oil","description":"We recommend having it in your trunk for every car trip.","buy_urls":["https://en.wikipedia.org/wiki/Motor_oil"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/245/medium/MotorOil.jpg?1451154281","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/245/thumb/MotorOil.jpg?1451154281"},{"id":300,"item_category_id":1,"name":"Mountaineering boots","description":"Boots should be waterproof, lightweight, designed with crampon climbing as a focus, but able to climb rock without crampon, with stiff platform of the rigid or semi rigid sole.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=mountain%20boots\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/300/medium/MountainBoots.jpg?1449418940","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/300/thumb/MountainBoots.jpg?1449418940"},{"id":404,"item_category_id":11,"name":"MP3 player","description":"","buy_urls":["https://en.wikipedia.org/wiki/MP3_player"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/404/medium/Mp3Player.jpg?1452019981","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/404/thumb/Mp3Player.jpg?1452019981"},{"id":336,"item_category_id":2,"name":"Mug","description":"You may eat and drink from the mug.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=mugs\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/336/medium/Mug.jpg?1449419059","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/336/thumb/Mug.jpg?1449419059"},{"id":254,"item_category_id":15,"name":"Multi-tool","description":"It is useful for minor repairs of equipment.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=multi%20tools\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/254/medium/Multitool.jpg?1449419107","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/254/thumb/Multitool.jpg?1449419107"},{"id":250,"item_category_id":15,"name":"Nails","description":"Not a frequently used item, but having a hangful of nails in your toolbox gives you a chance to make a bench or a table if needed.","buy_urls":["https://en.wikipedia.org/wiki/Nail_(fastener)"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/250/medium/Nails.jpg?1451153132","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/250/thumb/Nails.jpg?1451153132"},{"id":222,"item_category_id":5,"name":"Nail scissors","description":"Keep your fingers and nails clear and maintained.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=scissors\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/222/medium/NailScissors.jpg?1449419399","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/222/thumb/NailScissors.jpg?1449419399"},{"id":226,"item_category_id":5,"name":"Napkins","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/226/medium/Napkins.jpg?1452021077","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/226/thumb/Napkins.jpg?1452021077"},{"id":283,"item_category_id":4,"name":"Nasal drops","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/283/medium/NasalDrops.jpg?1452023564","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/283/thumb/NasalDrops.jpg?1452023564"},{"id":411,"item_category_id":8,"name":"Notebook","description":"Useful for travel writing and calculations. It can be used to ignite the fire too.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/411/medium/Notebook.jpg?1451152698","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/411/thumb/Notebook.jpg?1451152698"},{"id":286,"item_category_id":4,"name":"Oral rehydration solution","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Oral_rehydration_therapy"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/286/medium/Antihistamine.jpg?1452020548","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/286/thumb/Antihistamine.jpg?1452020548"},{"id":280,"item_category_id":4,"name":"Pain reliever","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Analgesic"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/280/medium/Antibacterials.jpg?1452018459","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/280/thumb/Antibacterials.jpg?1452018459"},{"id":281,"item_category_id":4,"name":"Pain reliever for sprains and strains","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Strain_(injury)"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/281/medium/Antihistamine.jpg?1452018550","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/281/thumb/Antihistamine.jpg?1452018550"},{"id":329,"item_category_id":1,"name":"Pants","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=pants"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/329/medium/Pants.jpg?1449419805","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/329/thumb/Pants.jpg?1449419805"},{"id":237,"item_category_id":8,"name":"Passport","description":"Don`t forget your passport when travelling abroad.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/237/medium/Passport.jpg?1452019799","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/237/thumb/Passport.jpg?1452019799"},{"id":412,"item_category_id":8,"name":"Pencil","description":"It`s durable markings are resistant to moisture, most chemicals, ultraviolet radiation, and natural aging.","buy_urls":["https://en.wikipedia.org/wiki/Pencil"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/412/medium/Pencil.jpg?1451154017","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/412/thumb/Pencil.jpg?1451154017"},{"id":402,"item_category_id":11,"name":"Phone car charger","description":"Keep your phone charged while on the road.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=car%20charger\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/402/medium/PhoneCarCharger.jpg?1449419889","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/402/thumb/PhoneCarCharger.jpg?1449419889"},{"id":399,"item_category_id":11,"name":"Phone charger","description":"Always keep your phone charged, when there is such a chance.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/399/medium/PhoneCharger.jpg?1451153730","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/399/thumb/PhoneCharger.jpg?1451153730"},{"id":398,"item_category_id":11,"name":"Photo camera","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/398/medium/PhotoCamera.jpeg?1453301166","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/398/thumb/PhotoCamera.jpeg?1453301166"},{"id":236,"item_category_id":8,"name":"Photocopies of documents","description":"Documents` copies will help you find a way out if originals are lost.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/236/medium/DocCopies.jpg?1451154129","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/236/thumb/DocCopies.jpg?1451154129"},{"id":364,"item_category_id":3,"name":"Pillow","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=pillows\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/364/medium/Pillow.jpg?1449419937","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/364/thumb/Pillow.jpg?1449419937"},{"id":339,"item_category_id":2,"name":"Plate","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=plates\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/339/medium/Plate.jpg?1449419993","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/339/thumb/Plate.jpg?1449419993"},{"id":248,"item_category_id":15,"name":"Pliers","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=pliers\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/248/medium/Pliers.jpg?1449420057","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/248/thumb/Pliers.jpg?1449420057"},{"id":216,"item_category_id":5,"name":"Pocket mirror","description":"Take care of yourself even while in the wilderness.","buy_urls":["https://en.wikipedia.org/wiki/Mirror"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/216/medium/PocketMirror.jpg?1451153812","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/216/thumb/PocketMirror.jpg?1451153812"},{"id":353,"item_category_id":3,"name":"Pocket trowel","description":"Designed to support the Leave No Trace ethic of minimizing backcountry impact by humans.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=pocket%20trowel\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/353/medium/PocketShovel.jpg?1451154079","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/353/thumb/PocketShovel.jpg?1451154079"},{"id":403,"item_category_id":11,"name":"Powerbank","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=power%20banks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/403/medium/Powerbank.jpg?1449420258","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/403/thumb/Powerbank.jpg?1449420258"},{"id":425,"item_category_id":3,"name":"Protection shorts","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=protection%20shorts\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/425/medium/ProtectionShorts.jpg?1452074534","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/425/thumb/ProtectionShorts.jpg?1452074534"},{"id":426,"item_category_id":3,"name":"Protective knee pads","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=protection%20pads\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/426/medium/ProtectionPads.jpg?1452074622","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/426/thumb/ProtectionPads.jpg?1452074622"},{"id":378,"item_category_id":3,"name":"Prusik","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=prusik%20cords\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/378/medium/Prusik.jpg?1449420360","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/378/thumb/Prusik.jpg?1449420360"},{"id":247,"item_category_id":15,"name":"Pump","description":"Recommended in every car trip.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/247/medium/Pump.jpg?1451154458","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/247/thumb/Pump.jpg?1451154458"},{"id":383,"item_category_id":3,"name":"Quickdraw with carabiners","description":"Suitable for all types of climbing. Select basing on strength abd weight.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=quickdraws\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/383/medium/Quickdraw.jpg?1449420412","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/383/thumb/Quickdraw.jpg?1449420412"},{"id":413,"item_category_id":1,"name":"Raincoat","description":"Protects your clothes and backpack from rain.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=rain%20coats\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/413/medium/Raincoat.jpg?1449420461","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/413/thumb/Raincoat.jpg?1449420461"},{"id":212,"item_category_id":5,"name":"Razor","description":"A razor is a bladed tool primarily used in the removal of unwanted body hair through the act of shaving","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/212/medium/Razor.jpg?1451152785","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/212/thumb/Razor.jpg?1451152785"},{"id":276,"item_category_id":4,"name":"Respiratory and ENT infection medication","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Broad-spectrum_antibiotic"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/276/medium/BroadSpectrum_Antibiotics.jpg?1452023125","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/276/thumb/BroadSpectrum_Antibiotics.jpg?1452023125"},{"id":348,"item_category_id":3,"name":"Rope","description":"Choose the rope depending on activity type and rope`s characteristics. Ropes for climbing and for clothes drying differ significantly.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ropes\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/348/medium/Rope.jpg?1449420527","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/348/thumb/Rope.jpg?1449420527"},{"id":255,"item_category_id":15,"name":"Saw","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=saws\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/255/medium/Saw.jpg?1449420564","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/255/thumb/Saw.jpg?1449420564"},{"id":379,"item_category_id":3,"name":"Screw-lock carabiner","description":"Keep in mind strength and weight parameters when selecting carabiners.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=screw-lock%20carabiners\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/379/medium/ScrewLock-Carab.jpg?1449420611","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/379/thumb/ScrewLock-Carab.jpg?1449420611"},{"id":291,"item_category_id":4,"name":"Sedative","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":["https://en.wikipedia.org/wiki/Sedative"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/291/medium/BroadSpectrum_Antibiotics.jpg?1452023651","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/291/thumb/BroadSpectrum_Antibiotics.jpg?1452023651"},{"id":262,"item_category_id":15,"name":"Sewing kit","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sewing%20kits\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/262/medium/SewingKit.jpg?1449420716","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/262/thumb/SewingKit.jpg?1449420716"},{"id":231,"item_category_id":5,"name":"Shampoo","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=shampoos\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/231/medium/Shampoo.jpg?1449420759","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/231/thumb/Shampoo.jpg?1449420759"},{"id":328,"item_category_id":1,"name":"Shorts","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=shorts\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/328/medium/Shorts.jpg?1449420806","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/328/thumb/Shorts.jpg?1449420806"},{"id":264,"item_category_id":15,"name":"Shovel","description":"Helps in setting up a camp or freeing a stuck vehicle.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=shovel\u0026s=u"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/264/medium/Shovel.jpg?1449420842","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/264/thumb/Shovel.jpg?1449420842"},{"id":414,"item_category_id":3,"name":"Signal mirror","description":"Will help you give a signal and draw rescuers' attention.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=signal%20mirror\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/414/medium/SugnalMirror.jpg?1449418739","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/414/thumb/SugnalMirror.jpg?1449418739"},{"id":408,"item_category_id":11,"name":"SIM card","description":"Simply, if I will be in a country for more than a day, I use a local SIM that I have used previously and can top-up online, or I purchase a SIM locally.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/408/medium/sim-card.jpg?1451143175","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/408/thumb/sim-card.jpg?1451143175"},{"id":359,"item_category_id":3,"name":"Sitting mat","description":"Make yourself confortable at camp and around a campfire.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sitting%20chairs\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/359/medium/SittingMat.jpg?1449420937","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/359/thumb/SittingMat.jpg?1449420937"},{"id":417,"item_category_id":3,"name":"Ski boots","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20boots\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/417/medium/SkiBoots.jpg?1452072909","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/417/thumb/SkiBoots.jpg?1452072909"},{"id":423,"item_category_id":3,"name":"Ski or snowboard bag","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20bags\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/423/medium/SkiBag.jpg?1452074311","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/423/thumb/SkiBag.jpg?1452074311"},{"id":418,"item_category_id":3,"name":"Ski poles","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20poles\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/418/medium/SkiPoles.jpg?1452072990","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/418/thumb/SkiPoles.jpg?1452072990"},{"id":357,"item_category_id":3,"name":"Skis","description":"Plan to take your own or rental skis, when you go skiing.\r\nIt is easier to move around on skis through deep snow in winter hike.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=skis\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/357/medium/Skis.jpg?1449421032","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/357/thumb/Skis.jpg?1449421032"},{"id":368,"item_category_id":3,"name":"Sleeping bag","description":"Choose a sleeping bag taking into account your travel region's night temperatures.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sleeping%20bags\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/368/medium/SleepingBag.jpg?1449421080","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/368/thumb/SleepingBag.jpg?1449421080"},{"id":355,"item_category_id":3,"name":"Sleeping mat","description":"Choose a right pad for a comfortable and supportive sleep in expected temperature.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sleeping%20mats\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/355/medium/SleepingMat.jpg?1449421131","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/355/thumb/SleepingMat.jpg?1449421131"},{"id":415,"item_category_id":3,"name":"Snowboard","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=snowboard\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/415/medium/Snowboard.jpg?1452072704","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/415/thumb/Snowboard.jpg?1452072704"},{"id":416,"item_category_id":3,"name":"Snowboard boots","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=snowboard%20boots\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/416/medium/SnowboardBoots.jpg?1452072840","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/416/thumb/SnowboardBoots.jpg?1452072840"},{"id":310,"item_category_id":1,"name":"Socks","description":"It is important to have enough of comfortable socks, suitable for youe type of travel.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=socks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/310/medium/Socks.jpg?1449421194","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/310/thumb/Socks.jpg?1449421194"},{"id":337,"item_category_id":2,"name":"Spoon","description":"Better use spork.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=spoon%20fork%20knife\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/337/medium/Spoon.jpg?1449421261","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/337/thumb/Spoon.jpg?1449421261"},{"id":278,"item_category_id":4,"name":"Sticking plasters","description":"A small medical dressing used for injuries not serious enough to require a full-size bandage.","buy_urls":["https://en.wikipedia.org/wiki/Adhesive_bandage"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/278/medium/StickingPlasters.jpg?1451154231","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/278/thumb/StickingPlasters.jpg?1451154231"},{"id":229,"item_category_id":5,"name":"Sunburn relief","description":"","buy_urls":[],"photo":"","photo_thumb":""},{"id":318,"item_category_id":1,"name":"Sunglasses","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sunglass\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/318/medium/Sunglasses.jpg?1449421331","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/318/thumb/Sunglasses.jpg?1449421331"},{"id":227,"item_category_id":5,"name":"Sunscreen","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=sunscreens\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/227/medium/Sunscreen.jpg?1449421384","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/227/thumb/Sunscreen.jpg?1449421384"},{"id":304,"item_category_id":1,"name":"Swimsuit","description":"Pack it into your backpack if water treatment is expected.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=swimsuits\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/304/medium/Swimsuite.jpg?1449422991","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/304/thumb/Swimsuite.jpg?1449422991"},{"id":252,"item_category_id":15,"name":"Synthetic packthread","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/252/medium/Packthread.jpg?1452021018","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/252/thumb/Packthread.jpg?1452021018"},{"id":293,"item_category_id":14,"name":"Table","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=tables\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/293/medium/Table.jpg?1449423084","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/293/thumb/Table.jpg?1449423084"},{"id":261,"item_category_id":15,"name":"Tape","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/261/medium/tape.jpeg?1453300044","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/261/thumb/tape.jpeg?1453300044"},{"id":363,"item_category_id":3,"name":"Tent","description":"Protects you and your stuff from wind, rain and sun. Choose a tend basing on weather conditions, type of travel and weight.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=tents\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/363/medium/tent.jpg?1449423197","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/363/thumb/tent.jpg?1449423197"},{"id":259,"item_category_id":15,"name":"Tent repair kit","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=tent%20repair%20accessories\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/259/medium/TentRepair.jpg?1449423303","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/259/thumb/TentRepair.jpg?1449423303"},{"id":232,"item_category_id":8,"name":"Tickets","description":"Take care of the train, plane and bus tickets.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/232/medium/Tickets.jpg?1451152502","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/232/thumb/Tickets.jpg?1451152502"},{"id":344,"item_category_id":16,"name":"Tick repellent","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=tick%20repellent\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/344/medium/InsectsRepell.jpg?1449423339","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/344/thumb/InsectsRepell.jpg?1449423339"},{"id":230,"item_category_id":5,"name":"Toilet paper","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/230/medium/ToiletPaper.jpeg?1453300806","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/230/thumb/ToiletPaper.jpeg?1453300806"},{"id":220,"item_category_id":5,"name":"Toilet soap","description":"Hygienic procedures are also useful to set the mood.","buy_urls":["https://en.wikipedia.org/wiki/Soap"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/220/medium/ToiletSoap.jpg?1451154324","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/220/thumb/ToiletSoap.jpg?1451154324"},{"id":218,"item_category_id":5,"name":"Toothbrush","description":"Most of the cleaning is achieved by the mechanical action of the toothbrush, and not by the toothpaste.","buy_urls":["https://en.wikipedia.org/wiki/Toothbrush"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/218/medium/ToothBrush.jpg?1451153915","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/218/thumb/ToothBrush.jpg?1451153915"},{"id":217,"item_category_id":5,"name":"Toothpaste","description":"Toothpaste is commonly used in conjunction with a toothbrush to increase the effectiveness of tooth brushing. ","buy_urls":["https://en.wikipedia.org/wiki/Toothpaste"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/217/medium/ToothPaste.jpg?1451153866","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/217/thumb/ToothPaste.jpg?1451153866"},{"id":219,"item_category_id":5,"name":"Toothpicks","description":"A toothpick is a small stick of wood, plastic, bamboo used to remove detritus from the teeth, usually after a meal.","buy_urls":["https://en.wikipedia.org/wiki/Toothpick"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/219/medium/ToothPicks.jpg?1451153973","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/219/thumb/ToothPicks.jpg?1451153973"},{"id":224,"item_category_id":5,"name":"Towel","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=towels\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/224/medium/Towel.jpg?1449423433","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/224/thumb/Towel.jpg?1449423433"},{"id":242,"item_category_id":15,"name":"Tow rope","description":"May come in handy either on the road, or in the forest, or, especially, in the muddy bogs.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/242/medium/TowRope.jpg?1451152830","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/242/thumb/TowRope.jpg?1451152830"},{"id":362,"item_category_id":3,"name":"Trash bags","description":"Help you leave no trail","buy_urls":["https://en.wikipedia.org/wiki/Bin_bag"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/362/medium/TrashBags.jpg?1452019443","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/362/thumb/TrashBags.jpg?1452019443"},{"id":372,"item_category_id":3,"name":"Trekking poles","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=poles%20trekking\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/372/medium/TrekkingPoles.jpg?1449423515","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/372/thumb/TrekkingPoles.jpg?1449423515"},{"id":327,"item_category_id":1,"name":"T-shirt","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=t-shirts\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/327/medium/TShirt.jpg?1449423554","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/327/thumb/TShirt.jpg?1449423554"},{"id":386,"item_category_id":3,"name":"Tubular belay device","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=belay%20devices\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/386/medium/TubularBelay.jpg?1449423606","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/386/thumb/TubularBelay.jpg?1449423606"},{"id":410,"item_category_id":5,"name":"Tweezers","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=tweezers\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/410/medium/Tweezers.jpg?1449423637","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/410/thumb/Tweezers.jpg?1449423637"},{"id":309,"item_category_id":1,"name":"Underwear","description":"Choose comfortable underwear for the selected type of adventure and according to weather conditions.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=underwear\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/309/medium/Underwear.jpg?1449423701","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/309/thumb/Underwear.jpg?1449423701"},{"id":340,"item_category_id":2,"name":"Vacuum flask","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=vacuum%20flasks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/340/medium/VacuumFlask.jpg?1449423741","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/340/thumb/VacuumFlask.jpg?1449423741"},{"id":240,"item_category_id":8,"name":"Vehicle registration certificate","description":"","buy_urls":[],"photo":"","photo_thumb":""},{"id":302,"item_category_id":1,"name":"Vest","description":"For the additional warmth you need when you are running, cycling, climbing, skiing, hiking, or camping.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=vests\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/302/medium/Vest.jpg?1449423799","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/302/thumb/Vest.jpg?1449423799"},{"id":320,"item_category_id":1,"name":"Warm fleece hoodie","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=warm%20hoodie\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/320/medium/WarmFleeceHoodie.jpg?1449423855","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/320/thumb/WarmFleeceHoodie.jpg?1449423855"},{"id":322,"item_category_id":1,"name":"Warm gloves","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=warm%20gloves\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/322/medium/WarmGloves.jpg?1449423903","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/322/thumb/WarmGloves.jpg?1449423903"},{"id":321,"item_category_id":1,"name":"Warm socks","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=warm%20socks\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/321/medium/WarmSocks.jpg?1449424006","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/321/thumb/WarmSocks.jpg?1449424006"},{"id":374,"item_category_id":3,"name":"Watch","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=watches\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/374/medium/Watch.jpg?1449423966","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/374/thumb/Watch.jpg?1449423966"},{"id":332,"item_category_id":2,"name":"Water bottle","description":"Every time you leave a camp you should have it filled with water and at hand.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=water%20bottles\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/332/medium/WaterBottle.jpg?1449424063","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/332/thumb/WaterBottle.jpg?1449424063"},{"id":373,"item_category_id":3,"name":"Water filter","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=water%20filters\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/373/medium/WaterFilter.jpg?1449424175","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/373/thumb/WaterFilter.jpg?1449424175"},{"id":350,"item_category_id":3,"name":"Waterproof bag","description":"Provides you a safe and dry place to keep your clothes, sleeping bag and documents in dry and protect them from rain or river water.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=waterproof%20bags\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/350/medium/WaterproofBag.jpg?1449424229","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/350/thumb/WaterproofBag.jpg?1449424229"},{"id":234,"item_category_id":8,"name":"Waterproof bag for docs, cash, phone","description":"Always keep you documents, money, tickets and useful notes in dry and waterproof storage.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=document%20holder\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/234/medium/WaterProofDocs.jpg?1449424384","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/234/thumb/WaterProofDocs.jpg?1449424384"},{"id":303,"item_category_id":1,"name":"Water suit","description":"Saves your warmth and protects your body during caving trips.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=water+suits\u0026s=u"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/303/medium/WaterSuite.jpg?1449417617","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/303/thumb/WaterSuite.jpg?1449417617"},{"id":214,"item_category_id":5,"name":"Wet wipes","description":"As a personal hygiene item, may be used during walks, at camp and on the road.","buy_urls":["https://en.wikipedia.org/wiki/Wet_wipe"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/214/medium/WetWipes.jpg?1451153038","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/214/thumb/WetWipes.jpg?1451153038"},{"id":367,"item_category_id":3,"name":"Whistle","description":"Will help you give a signal to your friends or rescuers.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=whistles\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/367/medium/Whistle.jpg?1449424449","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/367/thumb/Whistle.jpg?1449424449"},{"id":330,"item_category_id":1,"name":"Windbreaker","description":"Recommended even in summer hikes, when it is easy to catch cold on the light wind being warmed up after a walk.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=windbreakers\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/330/medium/Windbreaker.jpg?1449424538","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/330/thumb/Windbreaker.jpg?1449424538"},{"id":297,"item_category_id":1,"name":"Winter Buff","description":"May be used as a hat, scarf, bandana, head or wrist band.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=winter%20buff\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/297/medium/WinterBuff.jpg?1449424586","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/297/thumb/WinterBuff.jpg?1449424586"},{"id":308,"item_category_id":1,"name":"Winter jacket","description":"Will save your warmth during winter and alpine hikes.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=winter%20jackets\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/308/medium/WinterJacket.jpg?1449424641","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/308/thumb/WinterJacket.jpg?1449424641"},{"id":419,"item_category_id":3,"name":"Winter sports gloves","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20gloves\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/419/medium/WinterSportsGloves.jpg?1452073787","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/419/thumb/WinterSportsGloves.jpg?1452073787"},{"id":421,"item_category_id":1,"name":"Winter sports jacket","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20jackets\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/421/medium/WinterSportsJacket.jpg?1452074023","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/421/thumb/WinterSportsJacket.jpg?1452074023"},{"id":420,"item_category_id":1,"name":"Winter sports pants","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=ski%20pants\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/420/medium/WinterSportsPants.jpg?1452073933","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/420/thumb/WinterSportsPants.jpg?1452073933"},{"id":256,"item_category_id":15,"name":"Wire","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=wire\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/256/medium/Wire.jpg?1449424699","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/256/thumb/Wire.jpg?1449424699"},{"id":388,"item_category_id":3,"name":"Wire rope","description":"","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/388/medium/WireRope.jpeg?1453300739","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/388/thumb/WireRope.jpeg?1453300739"},{"id":253,"item_category_id":15,"name":"Work gloves","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=work%20gloves\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/253/medium/ExtraWorkGloves.jpg?1449424764","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/253/thumb/ExtraWorkGloves.jpg?1449424764"},{"id":285,"item_category_id":4,"name":"Wound gel/spray","description":"Advise your doctor to get the right medicine prescribed.","buy_urls":[],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/285/medium/WoundSpray.jpg?1452020159","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/285/thumb/WoundSpray.jpg?1452020159"},{"id":246,"item_category_id":15,"name":"Wrench set","description":"In autotravel small repairs may be required at any time, be prepared for it.","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=wrench%20set\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/246/medium/WrenchSet.jpg?1449424813","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/246/thumb/WrenchSet.jpg?1449424813"},{"id":427,"item_category_id":3,"name":"Wrist guards","description":"","buy_urls":["http://www.backcountry.com/Store/catalog/search.jsp?q=wrist%20guards\u0026s=a"],"photo":"http://hikapro.com/system/backpack/items/photos/000/000/427/medium/WristGuards.jpg?1452074745","photo_thumb":"http://hikapro.com/system/backpack/items/photos/000/000/427/thumb/WristGuards.jpg?1452074745"}] \ No newline at end of file diff --git a/app/src/main/assets/jsons/sets.json b/app/src/main/assets/jsons/sets.json new file mode 100644 index 0000000..afc170e --- /dev/null +++ b/app/src/main/assets/jsons/sets.json @@ -0,0 +1 @@ +[{"id":4,"item_ids":[212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,254,256,258,259,261,253,257,262,270,275,296,295,297,300,298,308,306,309,310,311,313,318,319,320,321,322,315,323,325,326,327,329,331,333,335,336,337,338,339,340,341,377,348,349,351,352,391,379,353,380,355,381,382,383,358,360,361,362,363,378,384,365,366,367,385,368,386,369,387,371,372,388,374,394,403,395,400,397,398],"name":"Alpine","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":5,"item_ids":[212,213,214,216,217,218,220,221,222,224,225,226,227,228,229,230,231,233,235,236,237,238,239,240,241,242,249,250,243,244,264,251,245,246,247,255,248,256,252,261,253,293,294,299,304,305,309,310,312,317,318,327,328,329,331,333,334,335,336,337,338,343,339,341,342,344,345,346,347,348,353,355,359,362,363,364,368,369,370,374,394,402,400,401,398,258,259,262,275,270,406,223,215,409,232,411,412],"name":"Caravanning","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":8,"item_ids":[212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,228,230,231,232,233,234,235,236,237,238,239,254,256,258,259,260,261,253,257,262,270,275,299,303,305,306,309,310,311,312,314,316,317,321,315,327,328,329,331,332,333,334,335,336,337,338,343,339,340,341,342,344,345,346,377,389,390,348,349,352,379,353,380,383,358,392,362,363,364,378,384,365,366,367,368,386,369,387,370,371,388,373,374,403,395,391,396,400,397,398],"name":"Caving","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":3,"item_ids":[279,282,273,274,288,283,291,289,271,267,268,269,270,266,278,286,277,272,280,281,265,284,285,287,276,290,292],"name":"First-aid kit","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":15,"item_ids":[],"name":"My list","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":2,"item_ids":[212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,238,249,264,254,255,258,259,252,261,253,262,270,275,299,303,304,306,309,310,311,312,316,317,318,327,328,329,331,332,333,334,335,336,337,338,343,339,340,341,342,344,345,346,375,347,348,350,353,359,362,363,364,365,367,368,376,369,370,374,403,395,400,397,398],"name":"Rafting","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":14,"item_ids":[409,217,218,224,225,227,232,234,235,238,254,275,292,295,296,297,421,306,309,310,311,317,318,315,323,327,329,420,332,340,416,427,424,426,425,380,357,417,418,422,419,415,423,374,408,405,404,400,401,428],"name":"Ski / Snowboard","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":1,"item_ids":[212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,254,256,258,259,261,253,262,270,275,296,299,304,330,306,309,310,311,312,316,317,318,320,321,325,326,327,328,329,331,332,333,334,335,336,337,338,343,339,341,342,344,345,346,347,348,349,352,353,354,355,356,360,361,362,363,364,365,367,368,369,370,372,373,374,394,403,395,400,397,398,413],"name":"Summer hiking","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":6,"item_ids":[212,213,214,215,216,217,218,219,220,222,224,225,226,228,231,232,233,235,236,237,238,239,275,299,305,309,310,311,317,318,325,326,327,328,329,332,374,399,400,401,393,398,405,404,340],"name":"Trip","photo":"","photo_thumb":"","photo_thumbnail":""},{"id":10,"item_ids":[223,214,215,409,217,218,219,220,224,225,226,230,234,235,239,264,261,253,270,275,295,297,300,298,308,309,310,311,313,318,320,321,315,324,326,329,332,333,335,336,337,338,339,340,341,331,349,352,353,354,355,356,357,361,362,363,364,365,366,367,368,369,372,374,394,408,395,400,397],"name":"Winter hiking","photo":"","photo_thumb":"","photo_thumbnail":""}] \ No newline at end of file diff --git a/app/src/main/java/hikapro/com/backpack/App.java b/app/src/main/java/com/hikapro/backpack/App.java similarity index 58% rename from app/src/main/java/hikapro/com/backpack/App.java rename to app/src/main/java/com/hikapro/backpack/App.java index 701539b..5ca45f6 100644 --- a/app/src/main/java/hikapro/com/backpack/App.java +++ b/app/src/main/java/com/hikapro/backpack/App.java @@ -1,7 +1,8 @@ -package hikapro.com.backpack; +package com.hikapro.backpack; import android.app.Application; import android.content.Context; +import android.graphics.Typeface; /** * Created by tariel on 27/04/16. @@ -9,14 +10,20 @@ import android.content.Context; public class App extends Application { private static Context context; + private static Typeface mainFace; @Override public void onCreate() { super.onCreate(); App.context = getApplicationContext(); + App.mainFace = Typeface.createFromAsset(App.context.getAssets(), "fonts/Ubuntu-B.ttf"); } public static Context getAppContext() { return App.context; } + + public static Typeface getMainFace() { + return App.mainFace; + } } diff --git a/app/src/main/java/hikapro/com/backpack/MainActivity.java b/app/src/main/java/com/hikapro/backpack/MainActivity.java similarity index 64% rename from app/src/main/java/hikapro/com/backpack/MainActivity.java rename to app/src/main/java/com/hikapro/backpack/MainActivity.java index 135c449..a30de4c 100644 --- a/app/src/main/java/hikapro/com/backpack/MainActivity.java +++ b/app/src/main/java/com/hikapro/backpack/MainActivity.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack; +package com.hikapro.backpack; import android.app.Activity; import android.app.Fragment; @@ -6,23 +6,31 @@ import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.util.Log; +import android.view.MenuItem; -import hikapro.com.backpack.model.DetailModel; -import hikapro.com.backpack.model.ItemModel; -import hikapro.com.backpack.model.PackedModel; -import hikapro.com.backpack.model.SetModel; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.ItemDetailPresenter; -import hikapro.com.backpack.presenter.ItemListPresenter; -import hikapro.com.backpack.presenter.PackedListPresenter; -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.SetListPresenter; -import hikapro.com.backpack.view.View; -import hikapro.com.backpack.view.fragments.ItemDetailFragment; -import hikapro.com.backpack.view.fragments.ItemListFragment; -import hikapro.com.backpack.view.fragments.PackedListFragment; -import hikapro.com.backpack.view.fragments.SetListFragment; +import com.hikapro.backpack.model.AddModel; +import com.hikapro.backpack.model.DetailModel; +import com.hikapro.backpack.model.ItemModel; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.PackedModel; +import com.hikapro.backpack.model.SetModel; +import com.hikapro.backpack.model.ShareModel; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.AddPresenter; +import com.hikapro.backpack.presenter.ItemDetailPresenter; +import com.hikapro.backpack.presenter.ItemListPresenter; +import com.hikapro.backpack.presenter.PackedListPresenter; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.presenter.SetListPresenter; +import com.hikapro.backpack.presenter.SharePresenter; +import com.hikapro.backpack.view.View; +import com.hikapro.backpack.view.fragments.AddFragment; +import com.hikapro.backpack.view.fragments.ItemDetailFragment; +import com.hikapro.backpack.view.fragments.ItemListFragment; +import com.hikapro.backpack.view.fragments.PackedListFragment; +import com.hikapro.backpack.view.fragments.SetListFragment; +import com.hikapro.backpack.view.fragments.ShareFragment; public class MainActivity extends Activity implements View.ActivityCallback { @@ -34,6 +42,21 @@ public class MainActivity extends Activity implements View.ActivityCallback { // life cycle --> + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean ret; + switch (item.getItemId()) { + case android.R.id.home: + this.getFragmentManager().popBackStack(); + ret = true; + break; + default: + ret = super.onOptionsItemSelected(item); + } + return ret; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -42,7 +65,6 @@ public class MainActivity extends Activity implements View.ActivityCallback { stateMaintainer.init(); - if (fragmentManager.getBackStackEntryCount() == 0 && savedInstanceState == null) { startSetListFragment(); } else { @@ -82,6 +104,7 @@ public class MainActivity extends Activity implements View.ActivityCallback { presenter.setModel(model); model.setPresenter(presenter); } + fragment = fragmentManager.findFragmentByTag(PackedListFragment.class.getName()); if (fragment != null) { PackedListFragment view = (PackedListFragment) fragment; @@ -93,6 +116,30 @@ public class MainActivity extends Activity implements View.ActivityCallback { presenter.setModel(model); model.setPresenter(presenter); } + + fragment = fragmentManager.findFragmentByTag(ShareFragment.class.getName()); + if (fragment != null) { + ShareFragment view = (ShareFragment) fragment; + SharePresenter presenter = stateMaintainer.get(SharePresenter.class.getName()); + ShareModel model = stateMaintainer.get(ShareModel.class.getName()); + + view.setPresenter(presenter); + presenter.setView(view); + presenter.setModel(model); + model.setPresenter(presenter); + } + + fragment = fragmentManager.findFragmentByTag(AddFragment.class.getName()); + if (fragment != null) { + AddFragment view = (AddFragment) fragment; + AddPresenter presenter = stateMaintainer.get(AddPresenter.class.getName()); + AddModel model = stateMaintainer.get(AddModel.class.getName()); + + view.setPresenter(presenter); + presenter.setView(view); + presenter.setModel(model); + model.setPresenter(presenter); + } } Log.i("On create", "Activity"); } @@ -147,7 +194,6 @@ public class MainActivity extends Activity implements View.ActivityCallback { presenter.setModel(model); model.setPresenter(presenter); - //replaceFragment(view, false, SetListFragment.class.getName(), false, false); replaceFragment(view, SetListFragment.class.getName(), 0); stateMaintainer.put(presenter); stateMaintainer.put(model); @@ -166,7 +212,6 @@ public class MainActivity extends Activity implements View.ActivityCallback { presenter.setModel(model); model.setPresenter(presenter); - //replaceFragment(view, true, ItemListFragment.class.getName(), true, true); replaceFragment(view, ItemListFragment.class.getName(), Presenter.ADD_TO_BACKSTACK | Presenter.TRANSITION_X); @@ -191,7 +236,6 @@ public class MainActivity extends Activity implements View.ActivityCallback { presenter.setModel(model); model.setPresenter(presenter); - //replaceFragment(view, true, ItemListFragment.class.getName(), true, true); replaceFragment(view, PackedListFragment.class.getName(), Presenter.ADD_TO_BACKSTACK | Presenter.TRANSITION_Y); @@ -201,10 +245,10 @@ public class MainActivity extends Activity implements View.ActivityCallback { } @Override - public void startItemDetailFragment(Item item) { + public void startItemDetailFragment(int setId, Model.Item baseModel, int position) { - ItemDetailFragment view = ItemDetailFragment.newFromItem(item); - ItemDetailPresenter presenter = new ItemDetailPresenter(); + ItemDetailFragment view = ItemDetailFragment.newInstance(setId); + ItemDetailPresenter presenter = new ItemDetailPresenter(baseModel, position); DetailModel model = new DetailModel(); view.setPresenter(presenter); @@ -212,28 +256,49 @@ public class MainActivity extends Activity implements View.ActivityCallback { presenter.setModel(model); model.setPresenter(presenter); - //replaceFragment(view, true, ItemDetailFragment.class.getName(), true, true); replaceFragment(view, ItemDetailFragment.class.getName(), Presenter.ADD_TO_BACKSTACK | Presenter.TRANSITION_X); stateMaintainer.put(presenter); stateMaintainer.put(model); + + } + + @Override + public void startShareFragment(int setId) { + + ShareFragment view = ShareFragment.construct(); + SharePresenter presenter = new SharePresenter(); + ShareModel model = new ShareModel(); + + view.setPresenter(presenter); + presenter.setView(view); + presenter.setModel(model); + model.setPresenter(presenter); + + replaceFragment(view, ShareFragment.class.getName(), + Presenter.ADD_TO_BACKSTACK | Presenter.TRANSITION_X); + + stateMaintainer.put(presenter); + stateMaintainer.put(model); } - private void replaceFragment(Fragment fragment, boolean addBackStack, String tag, - boolean transition, boolean x) { - FragmentTransaction transaction = fragmentManager.beginTransaction(); - if (transition) { - if (x) - transaction.setCustomAnimations(R.animator.slide_in_left_x, R.animator.slide_out_right_x, - R.animator.slide_in_right_x, R.animator.slide_out_left_x); - } + @Override + public void startAddFragment(Set set) { + AddFragment view = AddFragment.newFromSet(set); + AddPresenter presenter = new AddPresenter(); + AddModel model = new AddModel(); - transaction.replace(R.id.container, fragment, tag); - if (addBackStack) - transaction.addToBackStack(null); - transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - transaction.commit(); + view.setPresenter(presenter); + presenter.setView(view); + presenter.setModel(model); + model.setPresenter(presenter); + + replaceFragment(view, AddFragment.class.getName(), + Presenter.ADD_TO_BACKSTACK | Presenter.TRANSITION_X); + + stateMaintainer.put(presenter); + stateMaintainer.put(model); } private void replaceFragment(Fragment fragment, String tag, int flags) { diff --git a/app/src/main/java/hikapro/com/backpack/StateMaintainer.java b/app/src/main/java/com/hikapro/backpack/StateMaintainer.java similarity index 95% rename from app/src/main/java/hikapro/com/backpack/StateMaintainer.java rename to app/src/main/java/com/hikapro/backpack/StateMaintainer.java index 7e21532..c830f36 100644 --- a/app/src/main/java/hikapro/com/backpack/StateMaintainer.java +++ b/app/src/main/java/com/hikapro/backpack/StateMaintainer.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack; +package com.hikapro.backpack; import android.app.Fragment; import android.app.FragmentManager; @@ -79,7 +79,6 @@ public class StateMaintainer { public void put(String key, Object obj) { data.put(key, obj); - Log.i(this.toString(), String.format("Put object %s Total count %d", key, data.size())); } diff --git a/app/src/main/java/com/hikapro/backpack/model/AddModel.java b/app/src/main/java/com/hikapro/backpack/model/AddModel.java new file mode 100644 index 0000000..48d61ce --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/AddModel.java @@ -0,0 +1,154 @@ +package com.hikapro.backpack.model; + +import android.os.Message; +import android.widget.Toast; + +import com.hikapro.backpack.App; +import com.hikapro.backpack.model.dao.Command; +import com.hikapro.backpack.model.dao.DAO; +import com.hikapro.backpack.model.dao.Event; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +/** + * Created by tariel on 18/05/16. + */ +public class AddModel implements Model.Add { + + private Presenter.Add presenter; + private DAO dao; + private List filteredItems; + private List categoriesCache; + + public AddModel() { + this.filteredItems = new ArrayList<>(256); + this.categoriesCache = new ArrayList<>(20); + this.dao = DAO.getInstance(); + dao.registerObserver(this); + } + + @Override + public void onEvent(Message event) { + + switch (event.what) { + + case Event.ITEM_LIKE_LOAD_ERROR : + if (!filteredItems.isEmpty()) { + filteredItems.clear(); + } + notifyDataSetChanged(); + break; + + case Event.ITEM_CATEGORY_LOAD_ERROR : + break; + + case Event.ITEM_INSERT_ERROR : + Toast.makeText(App.getAppContext(), "Item not inserted!", Toast.LENGTH_SHORT).show(); + break; + + case Event.ITEM_LIKE_LOAD_COMPLETED : + filteredItems = (List) event.obj; + notifyDataSetChanged(); + break; + + case Event.ITEM_CATEGORY_LOAD_COMPLETED : + Hashtable res = (Hashtable)event.obj; + categoriesCache = new ArrayList<>(res.values()); // TODO check utilization + break; + + case Event.ITEM_INSERTED : + Toast.makeText(App.getAppContext(), "New item inserted", Toast.LENGTH_SHORT).show(); + break; + } + } + + @Override + public void onDestroy(boolean isConfigurationChanging) { + if ( !isConfigurationChanging ) { + presenter = null; + } + } + + @Override + public void executeQuery() { + Message command; + if (categoriesCache.isEmpty()) { + command = Message.obtain(); + command.what = Command.ITEM_GET_CATEGORIES; + dao.executeCommand(command); + } + } + + @Override + public void add(Item item, int setId) { + Message command; + command = Message.obtain(); + command.what = Command.ITEM_INSERT; + command.obj = item; + command.arg1 = setId; + dao.executeCommand(command); + } + + @Override + public void filter(String query, int checkThisSet) { + + if (!query.isEmpty()) { + Message command = Message.obtain(); + command.what = Command.ITEM_GET_LIKE; + command.arg1 = checkThisSet; + query = query.toLowerCase(); + command.obj = query; + dao.executeCommand(command); + } + } + + @Override + public void notifyDataSetChanged() { + boolean res = !filteredItems.isEmpty(); + presenter.notifyDataSetChanged(res); + } + + @Override + public int getItemsCount() { + return filteredItems.size(); + } + + @Override + public Item getItemByPosition(int position) { + return filteredItems.get(position); + } + + @Override + public int getCategoriesCount() { + return categoriesCache.size(); + } + + @Override + public Category getCategoryByPosition(int position) { + return categoriesCache.get(position); + } + + @Override + public Category getCategoryById(int id) { + for (Category category : categoriesCache) { + if (category.getId() == id) + return category; + } + return null; + } + + @Override + public void setPresenter(Presenter.Add presenter) { + this.presenter = presenter; + } + + @Override + public Presenter.Add getPresenter() { + return presenter; + } +} diff --git a/app/src/main/java/com/hikapro/backpack/model/Api.java b/app/src/main/java/com/hikapro/backpack/model/Api.java new file mode 100644 index 0000000..218f39d --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/Api.java @@ -0,0 +1,36 @@ +package com.hikapro.backpack.model; + +import java.util.List; + +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; + +import com.hikapro.backpack.model.entities.Timestamp; +import com.hikapro.backpack.model.entities.Updates; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + +/** + * Created by tariel on 19/04/16. + */ +public interface Api { + + @GET("api/v1/backpack/items") + Call> getItems(@Query("locale") String locale); + + @GET("api/v1/backpack/item_categories") + Call> getItemCategories(@Query("locale") String locale); + + @GET("api/v1/backpack/sets") + Call> getSets(@Query("locale") String locale); + + @GET("api/v1/backpack/updates/timestamp") + Call getTimestamp(); + + @GET("api/v1/backpack/updates/all") + Call getUpdates(@Query("timestamp") long timestamp); + + +} diff --git a/app/src/main/java/com/hikapro/backpack/model/DetailModel.java b/app/src/main/java/com/hikapro/backpack/model/DetailModel.java new file mode 100644 index 0000000..d3a8437 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/DetailModel.java @@ -0,0 +1,189 @@ +package com.hikapro.backpack.model; + +import android.graphics.Bitmap; +import android.os.Message; +import android.widget.Toast; + +import com.hikapro.backpack.App; +import com.hikapro.backpack.model.dao.Command; +import com.hikapro.backpack.model.dao.DAO; +import com.hikapro.backpack.model.dao.Event; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; + +import java.util.Collections; + +/** + * Created by tariel on 23/04/16. + */ +public class DetailModel implements Model.Detail { + + private Presenter.ItemDetail presenter; + private Item item; + private DAO dao; + private Bitmap pic; + private int currentSet; + + public DetailModel() { + this.dao = DAO.getInstance(); + } + + // detail --> + + @Override + public int getCount() { + return 1; + } + + @Override + public Item getCurrentItem() { + return item; + } + + @Override + public void setCurrentItem(Item item) { + this.item = item; + } + + @Override + public Bitmap getPicture() { + return pic; + } + // detail <-- + + // events --> + + @Override + public void notifyDataSetChanged() { + if (presenter != null) + presenter.notifyDataSetChanged(); + } + @Override + public void onDestroy(boolean isConfigurationChanging) { + if ( !isConfigurationChanging ) { + presenter = null; + } + dao.unregisterObserver(this); + } + + // events <-- + + // process --> + @Override + public void executeQuery() { + dao.registerObserver(this); + + Message command = Message.obtain(); + command.what = Command.ITEM_READ; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + + command = Message.obtain(); + command.what = Command.ITEM_GET_IMAGE; + command.arg1 = item.getId(); + dao.executeCommand(command); + } + + @Override + public void pendingRemove(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_PENDING_REMOVAL; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + } + } + + @Override + public void pendingRemoveCancel(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_REMOVAL_CANCEL; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + } + } + + @Override + public void packItem(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_PACK; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + } + } + + @Override + public void unpackItem(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_UNPACK; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + } + } + + // process <-- + + + @Override + public void setPresenter(Presenter.ItemDetail presenter) { + this.presenter = presenter; + this.currentSet = presenter.getSetId(); + } + + @Override + public Presenter.ItemDetail getPresenter() { + return presenter; + } + + @Override + public void onEvent(Message event) { + + switch (event.what) { + + case Event.ITEM_IMAGE_LOAD_ERROR: + pic = null; + if (presenter != null) + presenter.notifyPictureChanged(); + break; + case Event.ITEM_READ_ERROR: + Toast.makeText(App.getAppContext(), "Item read error", Toast.LENGTH_SHORT).show(); + break; + case Event.ITEM_PACK_ERROR: + break; + case Event.ITEM_UNPACK_ERROR: + break; + case Event.ITEM_PENDING_REMOVAL_ERROR: + break; + case Event.ITEM_REMOVAL_CANCEL_ERROR: + break; + case Event.ITEM_IMAGE_LOAD_COMPLETED: + pic = (Bitmap) event.obj; + if (presenter != null) + presenter.notifyPictureChanged(); + break; + case Event.ITEM_PACKED: + executeQuery(); + break; + case Event.ITEM_UNPACKED: + executeQuery(); + case Event.ITEM_PENDING_REMOVAL_COMPLETED: + executeQuery(); + case Event.ITEM_REMOVAL_CANCELED: + executeQuery(); + break; + case Event.ITEM_READ_COMPLETED: + item = (Item) event.obj; + notifyDataSetChanged(); + break; + } + + } +} diff --git a/app/src/main/java/com/hikapro/backpack/model/ItemModel.java b/app/src/main/java/com/hikapro/backpack/model/ItemModel.java new file mode 100644 index 0000000..b437c46 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/ItemModel.java @@ -0,0 +1,342 @@ +package com.hikapro.backpack.model; + + +import android.os.Message; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.List; + +import com.hikapro.backpack.App; +import com.hikapro.backpack.model.dao.Command; +import com.hikapro.backpack.model.dao.DAO; +import com.hikapro.backpack.model.dao.Event; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; + +/** + * Created by tariel on 22/04/16. + */ +public class ItemModel implements Model.Item { + + protected Presenter.ItemList presenter; + + protected DAO dao; + protected int currentSet; + protected int currentSetActiveItemsQty; + protected int packedQty; + protected Hashtable categoriesCache; + protected List itemsCache; + + protected Hashtable> items; + + public ItemModel() { + this.categoriesCache = new Hashtable<>(20, 0.9f); + this.itemsCache = new ArrayList<>(256); + + this.packedQty = -1; + this.dao = DAO.getInstance(); + } + + @Override + public void onEvent(Message event) { + + switch (event.what) { + case Event.SET_ITEMS_LOAD_ERROR : // TODO check + if (presenter != null) { + if (packedQty == -1) + packedQty = presenter.getCurrentSet().getPackedQty(); + presenter.notifyItemPackStatusChanged(); + } + break; + case Event.SET_UNPACK_ERROR : + break; + case Event.SET_RESTORE_ERROR : + break; + case Event.SET_GET_STAT_ERROR: + break; + case Event.ITEM_FROM_SET_ERROR : + break; + case Event.ITEM_DELETE_ERROR : + break; + case Event.ITEM_PACK_ERROR : + break; + case Event.ITEM_UNPACK_ERROR : + break; + case Event.ITEM_INSERT_ERROR : + break; + case Event.ITEM_PENDING_REMOVAL_ERROR: + break; + case Event.ITEM_REMOVAL_CANCEL_ERROR: + break; + case Event.ITEM_CATEGORY_LOAD_ERROR : + break; + case Event.SET_ITEMS_LIKE_ERROR: + if (!itemsCache.isEmpty()) { + itemsCache.clear(); + } + notifyDataSetChanged(); + break; + case Event.SET_ITEMS_LOAD_COMPLETED: + itemsCache = (List) event.obj; + notifyDataSetChanged(); + break; + case Event.SET_ITEMS_LIKE_COMPLETED: + itemsCache = (List) event.obj; + notifyDataSetChanged(); + break; + case Event.SET_GET_STAT_COMPLETED: + currentSetActiveItemsQty = event.arg1; + packedQty = event.arg2; + if (presenter != null) + presenter.notifyItemPackStatusChanged(); + break; + case Event.ITEM_PENDING_REMOVAL_COMPLETED: + executeQuery(); + break; + case Event.ITEM_REMOVAL_CANCELED: + executeQuery(); + break; + case Event.ITEM_READ_COMPLETED: + itemsCache.add((Item) event.obj); + Collections.sort(itemsCache); + notifyDataSetChanged(); + break; + case Event.SET_UNPACK_COMPLETED: + executeQuery(); + break; + case Event.ITEM_CATEGORY_LOAD_COMPLETED: + categoriesCache = (Hashtable)event.obj; + break; + case Event.ITEM_FROM_SET_DELETED: + executeQuery(); + break; + case Event.ITEM_DELETED : + break; + case Event.ITEM_PACKED : + executeQuery(); + break; + case Event.ITEM_UNPACKED : + executeQuery(); + break; + case Event.ITEM_INSERTED : + break; + case Event.SET_RESTORE_COMPLETED: + executeQuery(); + Toast.makeText(App.getAppContext(), "Restore completed", Toast.LENGTH_SHORT).show(); + break; + } + } + + @Override + public void executeQuery() { + Message command; + dao.registerObserver(this); + + command = Message.obtain(); + command.what = Command.SET_CLEAN_PACKED; + command.arg1 = currentSet; + dao.executeCommand(command); + + if (categoriesCache.isEmpty()) { + command = Message.obtain(); + command.what = Command.ITEM_GET_CATEGORIES; + dao.executeCommand(command); + } + command = Message.obtain(); + command.what = Command.SET_GET_ITEMS; + command.arg1 = currentSet; + dao.executeCommand(command); + + command = Message.obtain(); + command.what = Command.SET_GET_STAT; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } + + // categories --> + + @Override + public Category getCategoryByPosition(int position) { + Category ret = null; + ret = categoriesCache.get(itemsCache.get(position).getCategory()); + return ret; + + } + // categories <-- + + // items --> + + @Override + public Item findItem(int id) { + Item item = null; + for (Item i : itemsCache) { + if (i.getId() == id) { + item = i; + break; + } + } + return item; + } + @Override + public Item getItemByPosition(int position) { + Item ret = null; + ret = itemsCache.get(position); + return ret; + } + + @Override + public void filter(String query) { + + if (query.isEmpty()) { + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } else { + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS_LIKE; + command.arg1 = presenter.getCurrentSet().getId(); + command.obj = query; + dao.executeCommand(command); + } + } + + @Override + public int getHeaderId(int position) { + return itemsCache.get(position).getCategory(); + } + + @Override + public int getItemId(int position) { + return itemsCache.get(position).getId(); + } + + @Override + public int getItemsCount() { + return itemsCache.size(); + } + + @Override + public int getActiveItemsCount() { + return currentSetActiveItemsQty; + } + + @Override + public int getPackedQty() { + return packedQty; + } + // items <-- + + // events --> + + @Override + public void notifyDataSetChanged() { + if (presenter != null) + presenter.notifyDataSetChanged(); + } + + @Override + public void onDestroy(boolean isConfigurationChanging) { + if ( !isConfigurationChanging ) { + presenter = null; + } + dao.unregisterObserver(this); + } + // events <-- + + // process --> + + @Override + public void pendingRemove(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_PENDING_REMOVAL; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + //itemsCache.remove(item);// TODO check nn + } + } + + @Override + public void pendingRemoveCancel(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_REMOVAL_CANCEL; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + //itemsCache.remove(item); + } + } + + @Override + public void remove(Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_DELETE_FROM_SET; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + //itemsCache.remove(item); + } + } + @Override + public void packItem(com.hikapro.backpack.model.entities.Item item) { + if (item != null) { + itemsCache.remove(item); + Message command = Message.obtain(); + command.what = Command.ITEM_PACK; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + } + } + + @Override + public void unpackItem(com.hikapro.backpack.model.entities.Item item) { + // nothing + } + + @Override + public void unpackSet(int setId) { + Message command; + command = Message.obtain(); + command.what = Command.SET_UNPACK_ITEMS; + command.arg1 = setId; + dao.executeCommand(command); + } + + @Override + public void restoreSet(int setId) { + Message command; + command = Message.obtain(); + command.what = Command.SET_RESTORE_DEFAULT; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } + + // process <-- + + // other --> + + + @Override + public void setPresenter(Presenter.ItemList presenter) { + this.presenter = presenter; + this.currentSet = presenter.getCurrentSet().getId(); + this.currentSetActiveItemsQty = presenter.getCurrentSet().getActiveQty(); + //dao.registerObserver(this); + } + + @Override + public Presenter.ItemList getPresenter() { + return presenter; + } + + // other <-- +} diff --git a/app/src/main/java/com/hikapro/backpack/model/Model.java b/app/src/main/java/com/hikapro/backpack/model/Model.java new file mode 100644 index 0000000..c61dcaf --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/Model.java @@ -0,0 +1,94 @@ +package com.hikapro.backpack.model; + +import android.graphics.Bitmap; +import android.os.Message; + +import java.util.List; + +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; + +/** + * Created by tariel on 19/04/16. + */ +public interface Model { + + interface Base { + void onDestroy(boolean isConfigurationChanging); + void executeQuery(); + void notifyDataSetChanged(); + void onEvent(Message event); + } + + interface Set extends Base { + com.hikapro.backpack.model.entities.Set getSetByPosition(int position); + com.hikapro.backpack.model.entities.Set findSet(int id); + int getSetsCount(); + void setPresenter(Presenter.SetList presenter); + Presenter.SetList getPresenter(); + //GLM + List getSets(); // tag renamed + + void setsReorderNotify(); + } + + interface Item extends Base { + void filter(String query); + int getHeaderId(int position);//TODO review + int getItemId(int position);//TODO review + // leave at home + void pendingRemove(com.hikapro.backpack.model.entities.Item item); + void pendingRemoveCancel(com.hikapro.backpack.model.entities.Item item); + void remove(com.hikapro.backpack.model.entities.Item item); + int getItemsCount(); + int getActiveItemsCount(); + int getPackedQty(); + + com.hikapro.backpack.model.entities.Item findItem(int id); + com.hikapro.backpack.model.entities.Item getItemByPosition(int position); + + com.hikapro.backpack.model.entities.Category getCategoryByPosition(int position); + void setPresenter(Presenter.ItemList presenter); + Presenter.ItemList getPresenter(); + + void packItem(com.hikapro.backpack.model.entities.Item item); + void unpackItem(com.hikapro.backpack.model.entities.Item item); + void unpackSet(int setId); + void restoreSet(int setId); + } + + interface Detail extends Base { + int getCount(); + com.hikapro.backpack.model.entities.Item getCurrentItem(); + void setCurrentItem(com.hikapro.backpack.model.entities.Item item); + Bitmap getPicture(); + void setPresenter(Presenter.ItemDetail presenter); + Presenter.ItemDetail getPresenter(); + void pendingRemove(com.hikapro.backpack.model.entities.Item item); + void pendingRemoveCancel(com.hikapro.backpack.model.entities.Item item); + void packItem(com.hikapro.backpack.model.entities.Item item); + void unpackItem(com.hikapro.backpack.model.entities.Item item); + } + + interface Share extends Base { + void setPresenter(Presenter.Share presenter); + Presenter.Share getPresenter(); + + } + + interface Add extends Base { + void setPresenter(Presenter.Add presenter); + Presenter.Add getPresenter(); + void filter(String query, int checkThisSet); + int getItemsCount(); + com.hikapro.backpack.model.entities.Item getItemByPosition(int position); + int getCategoriesCount(); + Category getCategoryByPosition(int position); + Category getCategoryById(int id); + void add(com.hikapro.backpack.model.entities.Item item, int setId); + } + + + +} diff --git a/app/src/main/java/com/hikapro/backpack/model/NetworkUtil.java b/app/src/main/java/com/hikapro/backpack/model/NetworkUtil.java new file mode 100644 index 0000000..10d2728 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/NetworkUtil.java @@ -0,0 +1,43 @@ +package com.hikapro.backpack.model; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import java.net.InetAddress; + +/** + * Created by tariel on 29/05/16. + */ +public class NetworkUtil { + + public static boolean isConnectedToNetwork(Context context) { + ConnectivityManager cm = + (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + + NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + boolean isConnected = activeNetwork != null && + activeNetwork.isConnectedOrConnecting(); + return isConnected; + } + + public static boolean isInternetAvailable() { + boolean ret; + try { + InetAddress ipAddr = InetAddress.getByName("hikapro.com"); + ret = ipAddr.equals("") ? false : true; + } catch (Exception e) { + ret = false; + } + return ret; + } + + public static int getConnectionType(Context context) { + ConnectivityManager cm = + (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + + NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + return activeNetwork.getType(); + //ConnectivityManager.TYPE_WIFI; + } +} diff --git a/app/src/main/java/com/hikapro/backpack/model/PackedModel.java b/app/src/main/java/com/hikapro/backpack/model/PackedModel.java new file mode 100644 index 0000000..62f8beb --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/PackedModel.java @@ -0,0 +1,159 @@ +package com.hikapro.backpack.model; + +import android.os.Message; +import android.widget.Toast; + +import java.util.Collections; +import java.util.Hashtable; +import java.util.List; + +import com.hikapro.backpack.App; +import com.hikapro.backpack.model.dao.Command; +import com.hikapro.backpack.model.dao.Event; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; + +/** + * Created by tariel on 12/05/16. + */ +public class PackedModel extends ItemModel { + + public PackedModel() { + super(); + } + + @Override + public void executeQuery() { + Message command; + dao.registerObserver(this); + if (categoriesCache.isEmpty()) { + command = Message.obtain(); + command.what = Command.ITEM_GET_CATEGORIES; + dao.executeCommand(command); + } + command = Message.obtain(); + command.what = Command.SET_GET_PACKED_ITEMS; + command.arg1 = currentSet; + dao.executeCommand(command); + + command = Message.obtain(); + command.what = Command.SET_GET_STAT; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } + + @Override + public void filter(String query) { + + if (query.isEmpty()) { + Message command = Message.obtain(); + command.what = Command.SET_GET_PACKED_ITEMS; + command.arg1 = presenter.getCurrentSet().getId(); + dao.executeCommand(command); + } else { + Message command = Message.obtain(); + command.what = Command.SET_GET_ITEMS_LIKE_PACKED; + command.arg1 = presenter.getCurrentSet().getId(); + command.obj = query; + dao.executeCommand(command); + } + } + + @Override + public void onEvent(Message event) { + + switch (event.what) { + + case Event.SET_PACKED_LOAD_ERROR : + break; + case Event.SET_UNPACK_ERROR : + break; + case Event.SET_GET_STAT_ERROR: + break; + case Event.ITEM_CATEGORY_LOAD_ERROR : + break; + case Event.ITEM_PACK_ERROR : + break; + case Event.ITEM_UNPACK_ERROR : + break; + case Event.ITEM_PENDING_REMOVAL_ERROR: + break; + case Event.ITEM_REMOVAL_CANCEL_ERROR: + break; + case Event.SET_ITEMS_LIKE_PACKED_ERROR: + if (!itemsCache.isEmpty()) { + itemsCache.clear(); + } + notifyDataSetChanged(); + break; + case Event.SET_PACKED_LOAD_COMPLETED : + itemsCache = (List) event.obj; + notifyDataSetChanged(); + break; + case Event.SET_ITEMS_LIKE_PACKED_COMPLETED: + itemsCache = (List) event.obj; + notifyDataSetChanged(); + break; + case Event.SET_GET_STAT_COMPLETED: + currentSetActiveItemsQty = event.arg1; + packedQty = event.arg2; + if (presenter != null) + presenter.notifyItemPackStatusChanged(); + break; + case Event.ITEM_FROM_SET_DELETED: + executeQuery(); + break; + case Event.ITEM_PENDING_REMOVAL_COMPLETED:// TODO DEL + executeQuery(); + break; + case Event.ITEM_REMOVAL_CANCELED: + executeQuery(); + break; + case Event.ITEM_READ_COMPLETED: + itemsCache.add((Item) event.obj); + Collections.sort(itemsCache); + notifyDataSetChanged(); + break; + case Event.SET_UNPACK_COMPLETED : + executeQuery(); + break; + case Event.ITEM_CATEGORY_LOAD_COMPLETED : + categoriesCache = (Hashtable)event.obj; + break; + case Event.ITEM_PACKED : + executeQuery(); + break; + case Event.ITEM_UNPACKED : + executeQuery(); + break; + case Event.SET_RESTORE_COMPLETED: + executeQuery(); + Toast.makeText(App.getAppContext(), "Restore completed", Toast.LENGTH_SHORT).show(); + break; + } + } + + @Override + public void unpackItem(com.hikapro.backpack.model.entities.Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_UNPACK; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + itemsCache.remove(item); // TODO check nn + } + } + + @Override + public void packItem(com.hikapro.backpack.model.entities.Item item) { + if (item != null) { + Message command = Message.obtain(); + command.what = Command.ITEM_REMOVAL_CANCEL; + command.arg1 = currentSet; + command.arg2 = item.getId(); + dao.executeCommand(command); + itemsCache.remove(item); + } + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/RestClient.java b/app/src/main/java/com/hikapro/backpack/model/RestClient.java similarity index 94% rename from app/src/main/java/hikapro/com/backpack/model/RestClient.java rename to app/src/main/java/com/hikapro/backpack/model/RestClient.java index 6ae2133..f3874d7 100644 --- a/app/src/main/java/hikapro/com/backpack/model/RestClient.java +++ b/app/src/main/java/com/hikapro/backpack/model/RestClient.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model; +package com.hikapro.backpack.model; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/app/src/main/java/hikapro/com/backpack/model/SetModel.java b/app/src/main/java/com/hikapro/backpack/model/SetModel.java similarity index 60% rename from app/src/main/java/hikapro/com/backpack/model/SetModel.java rename to app/src/main/java/com/hikapro/backpack/model/SetModel.java index cea372c..53f62df 100644 --- a/app/src/main/java/hikapro/com/backpack/model/SetModel.java +++ b/app/src/main/java/com/hikapro/backpack/model/SetModel.java @@ -1,15 +1,18 @@ -package hikapro.com.backpack.model; +package com.hikapro.backpack.model; import android.os.Message; +import android.widget.Toast; import java.util.ArrayList; import java.util.List; -import hikapro.com.backpack.model.dao.Command; -import hikapro.com.backpack.model.dao.DAO; -import hikapro.com.backpack.model.dao.Event; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.Presenter; +import com.hikapro.backpack.App; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.dao.Command; +import com.hikapro.backpack.model.dao.DAO; +import com.hikapro.backpack.model.dao.Event; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.Presenter; /** @@ -17,7 +20,7 @@ import hikapro.com.backpack.presenter.Presenter; */ public class SetModel implements Model.Set { - private List cache; + private List cache; private Presenter.SetList presenter; private DAO dao; @@ -31,11 +34,11 @@ public class SetModel implements Model.Set { //region sets @Override - public hikapro.com.backpack.model.entities.Set getSetByPosition(int position) { + public com.hikapro.backpack.model.entities.Set getSetByPosition(int position) { return cache.get(position); } @Override - public hikapro.com.backpack.model.entities.Set findSet(int id) { + public com.hikapro.backpack.model.entities.Set findSet(int id) { Set ret = null; for (Set s : cache) { if (s.getId() == id) { @@ -67,18 +70,16 @@ public class SetModel implements Model.Set { } - private void sendMessage(String message) { - presenter.showMessage(message); - } - //endregion //region process @Override public void executeQuery() { + if (presenter != null) + presenter.startProgress(); Message command = Message.obtain(); - command.what = Command.SYNC_IF_NOT_EXISTS; + command.what = Command.SYNC; dao.executeCommand(command); command = Message.obtain(); command.what = Command.SET_GET_ALL; @@ -88,13 +89,33 @@ public class SetModel implements Model.Set { @Override public void onEvent(Message event) { switch (event.what) { + case Event.SYNC_NO_CONNECTION : + if (presenter != null) + presenter.stopProgress(); + Toast.makeText(App.getAppContext(), R.string.no_connection, Toast.LENGTH_SHORT).show(); + Message command = Message.obtain(); + command.what = Command.SYNC_READ_FROM_FILE; + dao.executeCommand(command); + command = Message.obtain(); + command.what = Command.SET_GET_ALL; + dao.executeCommand(command); + break; + case Event.SYNC_FAILED : + if (presenter != null) + presenter.stopProgress(); + Toast.makeText(App.getAppContext(), "SYNC FAILED", Toast.LENGTH_SHORT).show(); + break; case Event.SET_LOAD_ERROR : + if (presenter != null) + presenter.stopProgress(); break; case Event.SET_ITEMS_LOAD_ERROR : break; case Event.SET_REORDER_ERROR : break; case Event.SET_LOAD_COMPLETED : + if (presenter != null) + presenter.stopProgress(); cache = (List) event.obj; notifyDataSetChanged(); break; @@ -135,7 +156,7 @@ public class SetModel implements Model.Set { //GLM @Override - public List getSets() + public List getSets() { return cache; } diff --git a/app/src/main/java/com/hikapro/backpack/model/ShareModel.java b/app/src/main/java/com/hikapro/backpack/model/ShareModel.java new file mode 100644 index 0000000..4ba48fd --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/ShareModel.java @@ -0,0 +1,50 @@ +package com.hikapro.backpack.model; + +import android.os.Message; + +import com.hikapro.backpack.model.dao.DAO; +import com.hikapro.backpack.presenter.Presenter; + +/** + * Created by tariel on 16/05/16. + */ +public class ShareModel implements Model.Share { + + private Presenter.Share presenter; + private DAO dao; + + public ShareModel() { + this.dao = DAO.getInstance(); + dao.registerObserver(this); + } + + @Override + public void onDestroy(boolean isConfigurationChanging) { + + } + + @Override + public void executeQuery() { + + } + + @Override + public void notifyDataSetChanged() { + + } + + @Override + public void onEvent(Message event) { + + } + + @Override + public void setPresenter(Presenter.Share presenter) { + this.presenter = presenter; + } + + @Override + public Presenter.Share getPresenter() { + return presenter; + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/Command.java b/app/src/main/java/com/hikapro/backpack/model/dao/Command.java similarity index 67% rename from app/src/main/java/hikapro/com/backpack/model/dao/Command.java rename to app/src/main/java/com/hikapro/backpack/model/dao/Command.java index 204c349..71ee94a 100644 --- a/app/src/main/java/hikapro/com/backpack/model/dao/Command.java +++ b/app/src/main/java/com/hikapro/backpack/model/dao/Command.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.dao; +package com.hikapro.backpack.model.dao; /** * Created by tariel on 27/04/16. @@ -15,6 +15,11 @@ public interface Command { int SET_GET_ITEMS = 0x66; int SET_GET_PACKED_ITEMS = 0x67; int SET_UNPACK_ITEMS = 0x68; + int SET_RESTORE_DEFAULT = 0x69; + int SET_GET_STAT = 0x70; + int SET_CLEAN_PACKED = 0x71; + int SET_GET_ITEMS_LIKE = 0x72; + int SET_GET_ITEMS_LIKE_PACKED = 0x73; int ITEM_DELETE_FROM_SET = 0x78; int ITEM_INSERT = 0x79; @@ -22,6 +27,10 @@ public interface Command { int ITEM_UNPACK = 0x7B; int ITEM_GET_CATEGORIES = 0x7C; int ITEM_GET_IMAGE = 0x7D; + int ITEM_GET_LIKE = 0x7E; + int ITEM_PENDING_REMOVAL = 0x7F; + int ITEM_REMOVAL_CANCEL = 0x80; + int ITEM_READ = 0x81; int MY_LIST_POST = 0x8C; int MY_LIST_ITEM_ADD = 0x8D; @@ -30,6 +39,7 @@ public interface Command { int SYNC = 0xA0; int SYNC_IF_NOT_EXISTS = 0xA1; + int SYNC_READ_FROM_FILE = 0xA2; int TEST = 0xC8; diff --git a/app/src/main/java/com/hikapro/backpack/model/dao/DAO.java b/app/src/main/java/com/hikapro/backpack/model/dao/DAO.java new file mode 100644 index 0000000..3eb79c0 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/model/dao/DAO.java @@ -0,0 +1,1880 @@ +package com.hikapro.backpack.model.dao; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.hikapro.backpack.App; +import com.hikapro.backpack.model.Api; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.NetworkUtil; +import com.hikapro.backpack.model.RestClient; +import com.hikapro.backpack.model.SetModel; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.model.entities.Timestamp; +import com.hikapro.backpack.model.entities.UpdateLog; + +import retrofit2.Response; + +/** + * Created by tariel on 20/04/16. + */ +public class DAO { + //region Constants + private static final int CORE_POOL_SIZE = 1; + private static final int CORE_MAX_POOL_SIZE = 1; + + private static final int KEEP_ALIVE_TIME = 1; + private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; + + private static final int MY_LIST_ID = 15; + //endregion + + private static DAO instance; + + private final ThreadPoolExecutor threadPool; + private final Handler handler; + private SetModel setModel; + private final Api api; + private Context context; + private DbHelper helper; + private Map observers; + + private DAO() { + this.context = App.getAppContext(); + this.helper = new DbHelper(this.context); + this.api = RestClient.getApi(); + final BlockingQueue taskQueue = new LinkedBlockingQueue<>(); + this.threadPool = initPool(taskQueue); + this.observers = Collections.synchronizedMap(new HashMap()); + this.handler = initHandler(); + } + + static { + instance = new DAO(); + } + + public static DAO getInstance() { + return instance; + } + + public void registerObserver(Model.Base o) { + observers.put(o.getClass().getName(), o); + } + + public void unregisterObserver(Model.Base o) { + observers.remove(o.getClass().getName()); + } + + private ThreadPoolExecutor initPool (BlockingQueue taskQueue) { + ThreadPoolExecutor ret = new ThreadPoolExecutor( + CORE_POOL_SIZE + ,CORE_MAX_POOL_SIZE + ,KEEP_ALIVE_TIME + ,KEEP_ALIVE_TIME_UNIT + ,taskQueue + ); + return ret; + } + + private Handler initHandler() { + Handler ret = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + for (Map.Entry entry : observers.entrySet()) { + entry.getValue().onEvent(msg); + } + } + }; + return ret; + } + + public void executeCommand(Message command) { + + SetTask setTask; + ItemTask itemTask; + ImageProviderTask imageProviderTask; + + if (command != null) { + switch (command.what) { + + case Command.SET_GET_ALL: + setTask = new SetTask(Command.SET_GET_ALL, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + threadPool.execute(setTask); + break; + + case Command.SET_REORDER: + setTask = new SetTask(Command.SET_REORDER, + Process.THREAD_PRIORITY_BACKGROUND); + setTask.setsToUpdate = (List) command.obj; + threadPool.execute(setTask); + break; + + case Command.SET_GET_ITEMS: + setTask = new SetTask(Command.SET_GET_ITEMS, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.SET_GET_ITEMS_LIKE: + setTask = new SetTask(Command.SET_GET_ITEMS_LIKE, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + setTask.query = (String) command.obj; + threadPool.execute(setTask); + break; + + case Command.SET_GET_PACKED_ITEMS : + setTask = new SetTask(Command.SET_GET_PACKED_ITEMS, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.SET_GET_ITEMS_LIKE_PACKED: + setTask = new SetTask(Command.SET_GET_ITEMS_LIKE_PACKED, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + setTask.query = (String) command.obj; + threadPool.execute(setTask); + break; + + case Command.SET_UNPACK_ITEMS: + setTask = new SetTask(Command.SET_UNPACK_ITEMS, + Process.THREAD_PRIORITY_DEFAULT); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.SET_RESTORE_DEFAULT: + setTask = new SetTask(Command.SET_RESTORE_DEFAULT, + Process.THREAD_PRIORITY_DEFAULT); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.SET_GET_STAT: + setTask = new SetTask(Command.SET_GET_STAT, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.SET_CLEAN_PACKED: + setTask = new SetTask(Command.SET_CLEAN_PACKED, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + setTask.setId = command.arg1; + threadPool.execute(setTask); + break; + + case Command.ITEM_GET_CATEGORIES: + itemTask = new ItemTask(Command.ITEM_GET_CATEGORIES, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + threadPool.execute(itemTask); + break; + + case Command.ITEM_INSERT: + itemTask = new ItemTask(Command.ITEM_INSERT, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.item = (Item) command.obj; + itemTask.setId = command.arg1; + threadPool.execute(itemTask); + break; + + case Command.ITEM_READ: + itemTask = new ItemTask(Command.ITEM_READ, + Process.THREAD_PRIORITY_DEFAULT); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_DELETE_FROM_SET: + itemTask = new ItemTask(Command.ITEM_DELETE_FROM_SET, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_PENDING_REMOVAL: + itemTask = new ItemTask(Command.ITEM_PENDING_REMOVAL, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_REMOVAL_CANCEL: + itemTask = new ItemTask(Command.ITEM_REMOVAL_CANCEL, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_PACK : + itemTask = new ItemTask(Command.ITEM_PACK, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_UNPACK : + itemTask = new ItemTask(Command.ITEM_UNPACK, + Process.THREAD_PRIORITY_BACKGROUND); + itemTask.setId = command.arg1; + itemTask.itemId = command.arg2; + threadPool.execute(itemTask); + break; + + case Command.ITEM_GET_IMAGE : + Item item = findItem(command.arg1); + if (item != null) { + imageProviderTask = new ImageProviderTask(Command.ITEM_GET_IMAGE, + Process.THREAD_PRIORITY_DEFAULT); + imageProviderTask.item = item; + threadPool.execute(imageProviderTask); + } + break; + + case Command.ITEM_GET_LIKE : + itemTask = new ItemTask(Command.ITEM_GET_LIKE, + Process.THREAD_PRIORITY_MORE_FAVORABLE); + itemTask.query = (String) command.obj; + itemTask.setId = command.arg1; + threadPool.execute(itemTask); + break; + + case Command.MY_LIST_ITEM_ADD : + break; + + case Command.MY_LIST_ITEM_DELETE : + break; + + case Command.MY_LIST_POST : + break; + + case Command.MY_LIST_CLEAR : + break; + + case Command.SYNC: + threadPool.execute(new SyncTask(Command.SYNC, + Process.THREAD_PRIORITY_BACKGROUND)); + break; + + case Command.SYNC_READ_FROM_FILE: + threadPool.execute(new SyncTask(Command.SYNC_READ_FROM_FILE, + Process.THREAD_PRIORITY_BACKGROUND)); + break; + + case Command.SYNC_IF_NOT_EXISTS: + threadPool.execute(new SyncTask(Command.SYNC_IF_NOT_EXISTS, + Process.THREAD_PRIORITY_MORE_FAVORABLE)); + break; + } + } + } + + /////////////////////// DATABASE ///////////////////// + + //region Database + private SQLiteDatabase getReadDB(){ + return helper.getReadableDatabase(); + } + + private SQLiteDatabase getWriteDB(){ + return helper.getWritableDatabase(); + } + + // inserts + private void insertTimestamp(Timestamp timestamp) { + if (timestamp != null && timestamp.timestamp > 0) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + values = Db.LogTable.toContentValues(timestamp); + db.insert(Db.LogTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + + private void insertItems(List items) { + if (items != null && !items.isEmpty()) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Item item : items) { + values = Db.ItemsTable.toContentValues(item); + db.insert(Db.ItemsTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + + private long insertItem(Item item) { + long ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Cursor cursor = null; + if (item != null) { + try { + if (item.getId() < 0) { + db = getWriteDB(); + db.beginTransaction(); + String query = String.format("SELECT %s FROM %s WHERE %s = 1 ORDER BY %s DESC LIMIT 1", + Db.ItemsTable.COLUMN_ID, + Db.ItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_USER_DEFINED, + Db.ItemsTable.COLUMN_ID); + cursor = db.rawQuery(query, null); + + if (cursor.moveToNext()) { + int id = cursor.getInt(0); + item.setId(id + 1); + } else { + query = String.format("SELECT max(%s) FROM %s", + Db.ItemsTable.COLUMN_ID, + Db.ItemsTable.TABLE_NAME); + cursor = db.rawQuery(query, null); + if (cursor.moveToNext()) { + int maxId = cursor.getInt(0); + item.setId(maxId + 0x400); + } + } + values = Db.ItemsTable.toContentValues(item); + ret = db.insert(Db.ItemsTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } else { + ret = item.getId(); + } + } finally { + if (cursor != null) + cursor.close(); + if (db != null) { + db.endTransaction(); + db.close(); + } + } + } + return ret; + } + + private void insertCategories(List categories) { + + if (categories != null && !categories.isEmpty()) { + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Category category : categories) { + values = Db.CategoriesTable.toContentValues(category); + db.insert(Db.CategoriesTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + + private void insertSets(List sets) { + + if (sets != null && !sets.isEmpty()) { + ContentValues values; + int i = 0; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + for (Set set : sets) { + values = Db.SetsTable.toContentValues(set, i); + db.insert(Db.SetsTable.TABLE_NAME, null, values); + insertSetItems(set, db); + ++i; + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + } + } + + private void insertSetItems(Set set, SQLiteDatabase db) { + if (set != null && db != null) { + if (!set.getItems().isEmpty()) { + ContentValues values; + int setId = set.getId(); + try { + db.beginTransaction(); + for (Integer itemid : set.getItems()) { + values = Db.SetItemsTable.toContentValues(setId, itemid); + db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + } + } + + private long insertSetItem(int setId, int itemId, boolean userDefined) { + long ret = 0; + ContentValues values; + SQLiteDatabase db = getWriteDB(); + try { + db.beginTransaction(); + values = Db.SetItemsTable.toContentValues(setId, itemId, userDefined); + ret = db.insert(Db.SetItemsTable.TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + db.close(); + } + return ret; + } + + // reads + private boolean logExist() { + boolean ret; + SQLiteDatabase db = getReadDB(); + Cursor cursor = db.query(Db.LogTable.TABLE_NAME, + new String[] {Db.LogTable.COLUMN_ID}, null, null, null, null, null, "1"); + ret = cursor.moveToNext(); + cursor.close(); + db.close(); + return ret; + } + + private UpdateLog readLastLog() { + UpdateLog ret = null; + SQLiteDatabase db = null; + Cursor cursor = null; + try { + db = getReadDB(); + String q = String.format("SELECT * FROM %s ORDER BY %s DESC LIMIT 1", + Db.LogTable.TABLE_NAME, Db.LogTable.COLUMN_ID); + cursor = db.rawQuery(q,null); + if (cursor.moveToNext()) + ret = Db.LogTable.parseCursor(cursor); + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private Item findItem(int id) { + Item ret = null; + Cursor cursor = null; + SQLiteDatabase db = null; + try { + db = getReadDB(); + String query = String.format("SELECT * FROM %s a WHERE a.%s = %d LIMIT 1", + Db.ItemsTable.TABLE_NAME, Db.ItemsTable.COLUMN_ID, id); + cursor = db.rawQuery(query, null); + if (cursor.moveToNext()) { + ret = Db.ItemsTable.parseCursor(cursor); + } + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private Item readItem(SQLiteDatabase db, int setId, int itemId) { + Item ret = null; + Cursor cursor = null; + String query = String.format( + "SELECT a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, %s, %s FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND b.%s = ? LIMIT 1", + + Db.ItemsTable.COLUMN_ID, + Db.ItemsTable.COLUMN_NAME, + Db.ItemsTable.COLUMN_CATEGORY, + Db.ItemsTable.COLUMN_DESCRIPTION, + Db.ItemsTable.COLUMN_BUY_URLS, + Db.ItemsTable.COLUMN_PHOTO_URL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_URL, + Db.ItemsTable.COLUMN_PHOTO_LOCAL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_LOCAL, + Db.ItemsTable.COLUMN_USER_DEFINED, + Db.SetItemsTable.COLUMN_PENDING_REMOVAL, + Db.SetItemsTable.COLUMN_PACKED, + + Db.ItemsTable.TABLE_NAME, + Db.SetItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_ID, + Db.SetItemsTable.COLUMN_ITEM, + Db.SetItemsTable.COLUMN_SET, + Db.SetItemsTable.COLUMN_ITEM); + + try { + cursor = db.rawQuery(query, new String[]{String.valueOf(setId), String.valueOf(itemId)}); + if (cursor.moveToNext()) { + ret = Db.ItemsTable.parseCursor(cursor); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + } + return ret; + } + + private Item readItem(int setId, int itemId) { + Item ret = null; + Cursor cursor = null; + SQLiteDatabase db = null; + String query = String.format( + "SELECT a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, %s, %s FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND b.%s = ? LIMIT 1", + + Db.ItemsTable.COLUMN_ID, + Db.ItemsTable.COLUMN_NAME, + Db.ItemsTable.COLUMN_CATEGORY, + Db.ItemsTable.COLUMN_DESCRIPTION, + Db.ItemsTable.COLUMN_BUY_URLS, + Db.ItemsTable.COLUMN_PHOTO_URL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_URL, + Db.ItemsTable.COLUMN_PHOTO_LOCAL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_LOCAL, + Db.ItemsTable.COLUMN_USER_DEFINED, + Db.SetItemsTable.COLUMN_PENDING_REMOVAL, + Db.SetItemsTable.COLUMN_PACKED, + + Db.ItemsTable.TABLE_NAME, + Db.SetItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_ID, + Db.SetItemsTable.COLUMN_ITEM, + Db.SetItemsTable.COLUMN_SET, + Db.SetItemsTable.COLUMN_ITEM); + + try { + db = getReadDB(); + cursor = db.rawQuery(query, new String[]{String.valueOf(setId), String.valueOf(itemId)}); + if (cursor.moveToNext()) { + ret = Db.ItemsTable.parseCursor(cursor); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private List readItems(int setId, boolean packed) { + List ret = new ArrayList<>(256); + Cursor cursor = null; + SQLiteDatabase db = null; + Item item; + String query = String.format( + "SELECT a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, %s, %s FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND b.%s <> 1 AND b.%s %s 1", + + Db.ItemsTable.COLUMN_ID, + Db.ItemsTable.COLUMN_NAME, + Db.ItemsTable.COLUMN_CATEGORY, + Db.ItemsTable.COLUMN_DESCRIPTION, + Db.ItemsTable.COLUMN_BUY_URLS, + Db.ItemsTable.COLUMN_PHOTO_URL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_URL, + Db.ItemsTable.COLUMN_PHOTO_LOCAL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_LOCAL, + Db.ItemsTable.COLUMN_USER_DEFINED, + Db.SetItemsTable.COLUMN_PENDING_REMOVAL, + Db.SetItemsTable.COLUMN_PACKED, + + Db.ItemsTable.TABLE_NAME, + Db.SetItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_ID, + Db.SetItemsTable.COLUMN_ITEM, + Db.SetItemsTable.COLUMN_SET, + Db.SetItemsTable.COLUMN_DELETED, + Db.SetItemsTable.COLUMN_PACKED, + packed ? "=" : "<>"); + try { + db = getReadDB(); + cursor = db.rawQuery(query, new String[]{String.valueOf(setId)}); + while (cursor.moveToNext()) { + item = Db.ItemsTable.parseCursor(cursor); +// if (!packed && item.isPendingRemoval()) +// continue; + ret.add(item); + } + } catch (SQLiteException e) { + //TODO write to log here + ret = null; + + } catch (Exception e) { + //TODO write to log here + ret = null; + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private List readItemsLike(String like) { + List ret = new ArrayList<>(256); + Cursor cursor = null; + SQLiteDatabase db = null; + Item item; + String query = String.format("SELECT * FROM %s WHERE %s LIKE %s", + Db.ItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_NAME, + '\''+like+'%'+'\''); + try { + db = getReadDB(); + cursor = db.rawQuery(query, null); + while (cursor.moveToNext()) { + item = Db.ItemsTable.parseCursor(cursor); + ret.add(item); + } + } catch (SQLiteException e) { + e.toString(); + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private List readItemsLike(String like, int setId, boolean packed) { + List ret = new ArrayList<>(256); + Cursor cursor = null; + SQLiteDatabase db = null; + Item item; + String query = String.format( + "SELECT a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, %s, %s FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND a.%s LIKE %s AND b.%s <> 1 AND b.%s %s 1", + + Db.ItemsTable.COLUMN_ID, + Db.ItemsTable.COLUMN_NAME, + Db.ItemsTable.COLUMN_CATEGORY, + Db.ItemsTable.COLUMN_DESCRIPTION, + Db.ItemsTable.COLUMN_BUY_URLS, + Db.ItemsTable.COLUMN_PHOTO_URL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_URL, + Db.ItemsTable.COLUMN_PHOTO_LOCAL, + Db.ItemsTable.COLUMN_PHOTO_THUMB_LOCAL, + Db.ItemsTable.COLUMN_USER_DEFINED, + Db.SetItemsTable.COLUMN_PENDING_REMOVAL, + Db.SetItemsTable.COLUMN_PACKED, + + Db.ItemsTable.TABLE_NAME, + Db.SetItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_ID, + Db.SetItemsTable.COLUMN_ITEM, + Db.SetItemsTable.COLUMN_SET, + Db.ItemsTable.COLUMN_NAME, + '\''+like+'%'+'\'', + Db.SetItemsTable.COLUMN_DELETED, + Db.SetItemsTable.COLUMN_PACKED, + packed ? "=" : "<>"); + try { + db = getReadDB(); + cursor = db.rawQuery(query, new String[]{String.valueOf(setId)}); + while (cursor.moveToNext()) { + item = Db.ItemsTable.parseCursor(cursor); +// if (!packed && item.isPendingRemoval()) +// continue; + ret.add(item); + } + } catch (SQLiteException e) { + //TODO write to log here + ret = null; + } catch (Exception e) { + //TODO write to log here + ret = null; + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private List readItemsLike(String like, int checkThisSet) { + List ret = new ArrayList<>(256); + Cursor cursor = null; + Cursor cursor2 = null; + SQLiteDatabase db = null; + Item item; + String query = String.format("SELECT * FROM %s WHERE %s LIKE %s", + Db.ItemsTable.TABLE_NAME, + Db.ItemsTable.COLUMN_NAME, + '\''+like+'%'+'\''); + try { + db = getReadDB(); + cursor = db.rawQuery(query, null); + while (cursor.moveToNext()) { + item = Db.ItemsTable.parseCursor(cursor); + query = String.format("SELECT * FROM %s WHERE %s = %d AND %s = %d LIMIT 1", + Db.SetItemsTable.TABLE_NAME, + Db.SetItemsTable.COLUMN_ITEM, + item.getId(), + Db.SetItemsTable.COLUMN_SET, + checkThisSet + ); + cursor2 = db.rawQuery(query, null); + if (cursor2.moveToNext()) + item.InList = true; + ret.add(item); + } + } catch (SQLiteException e) { + e.toString(); + //TODO write to log here + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (cursor2 != null) + cursor2.close(); + if (db != null) + db.close(); + } + return ret; + } + + private Hashtable readCategories() { + Hashtable ret = new Hashtable<>(20, 0.9f); + Cursor cursor = null; + SQLiteDatabase db = null; + Category category; + try { + db = getReadDB(); + cursor = db.query(Db.CategoriesTable.TABLE_NAME, + new String[]{Db.CategoriesTable.COLUMN_ID, + Db.CategoriesTable.COLUMN_NAME}, + null,null,null,null,null); + while (cursor.moveToNext()) { + category = Db.CategoriesTable.parseCursor(cursor); + ret.put(category.getId(), category); + } + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private Set readSet(SQLiteDatabase db, int setId) { + Set ret = null; + Cursor cursor; + String query = String.format("SELECT * FROM %s a WHERE a.%s = %d LIMIT 1", + Db.SetsTable.TABLE_NAME, Db.SetsTable.COLUMN_ID, setId); + cursor = db.rawQuery(query, null); + if (cursor.moveToNext()) + ret = Db.SetsTable.parseCursor(cursor); + if (cursor != null) + cursor.close(); + return ret; + } + + private Set readSet(int setId) { + Set ret = null; + SQLiteDatabase db = null; + Cursor cursor = null; + try { + db = getReadDB(); + String query = String.format("SELECT * FROM %s a WHERE a.%s = %d LIMIT 1", + Db.SetsTable.TABLE_NAME, Db.SetsTable.COLUMN_ID, setId); + cursor = db.rawQuery(query, null); + if (cursor.moveToNext()) + ret = Db.SetsTable.parseCursor(cursor); + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + private List readSets() { + List ret = new ArrayList<>(12); + Cursor cursor = null; + SQLiteDatabase db = null; + Set set; + try { + db = getReadDB(); + String query = String.format("SELECT * FROM %s", Db.SetsTable.TABLE_NAME); + cursor = db.rawQuery(query, null); + while (cursor.moveToNext()) { + set = Db.SetsTable.parseCursor(cursor); + ret.add(set); + } + Collections.sort(ret); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (cursor != null) + cursor.close(); + if (db != null) + db.close(); + } + return ret; + } + + // updates + private int updateItemLocalPic(int id, String path) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + db = getWriteDB(); + db.beginTransaction(); + values = new ContentValues(); + values.put(Db.ItemsTable.COLUMN_PHOTO_LOCAL, path); + ret = db.update(Db.ItemsTable.TABLE_NAME, values, "_id = ?", + new String[]{String.valueOf(id)}); + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetsOrder(List reorderedSet) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + + db = getWriteDB(); + db.beginTransaction(); + for (Set set : reorderedSet) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_LINE_NUMBER, set.getLineNumber()); + ret += db.update(Db.SetsTable.TABLE_NAME, values, "_id = ?", + new String[]{String.valueOf(set.getId())}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetItemPendingRemoval(int setId, int itemId, boolean remove) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Set set; + Item item; + try { + db = getWriteDB(); + db.beginTransaction(); + item = readItem(db, setId, itemId); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PENDING_REMOVAL, remove); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + + set = readSet(db, setId); + if (set != null) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, remove ? set.getActiveQty()-1 : set.getActiveQty()+1); + if (item.isPacked() ) { + if (remove) + values.put(Db.SetsTable.COLUMN_PACKED_QTY, set.getPackedQty()-1); + else + values.put(Db.SetsTable.COLUMN_PACKED_QTY, set.getPackedQty()+1); + } + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(setId)}); + } + db.setTransactionSuccessful(); + + } catch (SQLiteException e) { + //TODO write to log here + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetItemDeleted(int setId, int itemId, boolean del) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Set set; + Item item; + try { + db = getWriteDB(); + db.beginTransaction(); + item = readItem(db, setId, itemId); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_DELETED, del); + values.put(Db.SetItemsTable.COLUMN_PENDING_REMOVAL, false); + values.put(Db.SetItemsTable.COLUMN_PACKED, false); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + + set = readSet(db, setId); + if (set != null) { + values = new ContentValues(); + if (del) { + if (!item.isPendingRemoval()) + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, set.getActiveQty()-1); + } else + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, set.getActiveQty()+1); + + values.put(Db.SetsTable.COLUMN_PACKED_QTY, item.isPacked() ? set.getPackedQty()-1 :set.getPackedQty()); + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(setId)}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetActiveQty(int setId, int updateOn) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Set set; + try { + db = getWriteDB(); + db.beginTransaction(); + set = readSet(db, setId); + if (set != null) { + values = new ContentValues(); + + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, set.getActiveQty()+updateOn); + + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(setId)}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + ret = -1; + } catch (Exception e) { + //TODO write to log here + ret = -1; + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetItemsCleanPacked(int setId) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + try { + db = getWriteDB(); + db.beginTransaction(); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PACKED, 0); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, + Db.SetItemsTable.COLUMN_PENDING_REMOVAL), + new String[]{String.valueOf(setId), "1"}); + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + ret = -1; + //TODO write to log here + } catch (Exception e) { + ret = -1; + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetItemsPacked(int setId, boolean pack) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Set set; + try { + db = getWriteDB(); + db.beginTransaction(); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PACKED, pack); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetItemsTable.COLUMN_SET), + new String[]{String.valueOf(setId)}); + + set = readSet(db, setId); + if (set != null) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_PACKED_QTY, pack ? set.getActiveQty() : 0); + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(setId)}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + ret = -1; + + } catch (Exception e) { + //TODO write to log here + ret = -1; + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetItemPacked(int setId, int itemId, boolean pack) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Set set; + Item item; + try { + db = getWriteDB(); + db.beginTransaction(); + item = readItem(db, setId, itemId); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PACKED, pack); + values.put(Db.SetItemsTable.COLUMN_PENDING_REMOVAL, 0); + ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", + Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), + new String[]{String.valueOf(setId), String.valueOf(itemId)}); + + set = readSet(db, setId); + if (set != null) { + values = new ContentValues(); + if (pack) { + values.put(Db.SetsTable.COLUMN_PACKED_QTY, set.getPackedQty() + 1); + if (item.isPendingRemoval()) + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, set.getActiveQty()+1 ); + } else { + if (!item.isPendingRemoval()) { + values.put(Db.SetsTable.COLUMN_PACKED_QTY, set.getPackedQty() - 1); + } else { + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, set.getActiveQty()+1); + } + } + + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(setId)}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetRestoreDefault(int setId) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + Set set; + try { + db = getWriteDB(); + db.beginTransaction(); + values = new ContentValues(); + values.put(Db.SetItemsTable.COLUMN_PACKED, false); + values.put(Db.SetItemsTable.COLUMN_DELETED, false); + values.put(Db.SetItemsTable.COLUMN_PENDING_REMOVAL, false); + ret = db.update(Db.SetItemsTable.TABLE_NAME, + values, + String.format("%s = ?", + Db.SetItemsTable.COLUMN_SET), + new String[]{String.valueOf(setId)}); + + set = readSet(db, setId); + if (set != null) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_PACKED_QTY, 0); + values.put(Db.SetsTable.COLUMN_ACTIVE_QTY, set.getItems().size()); + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(setId)}); + } + ret += db.delete(Db.SetItemsTable.TABLE_NAME, + String.format("%s = ? AND %s = ?", Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_USER_DEFINED), + new String[]{String.valueOf(setId), String.valueOf(1)}); + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + ret = -1; + } catch (Exception e) { + //TODO write to log here + ret = -1; + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateSetNames(List sets) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + + try { + db = getWriteDB(); + db.beginTransaction(); + for (Set set : sets) { + values = new ContentValues(); + values.put(Db.SetsTable.COLUMN_NAME, set.getName()); + ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", + Db.SetsTable.COLUMN_ID), + new String[]{String.valueOf(set.getId())}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateCategoryNames(List categories) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + + try { + db = getWriteDB(); + db.beginTransaction(); + for (Category category : categories) { + values = new ContentValues(); + values.put(Db.CategoriesTable.COLUMN_NAME, category.getName()); + ret += db.update(Db.CategoriesTable.TABLE_NAME, values, String.format("%s = ?", + Db.CategoriesTable.COLUMN_ID), + new String[]{String.valueOf(category.getId())}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateItemNames(List items) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + + try { + db = getWriteDB(); + db.beginTransaction(); + for (Item item : items) { + values = new ContentValues(); + values.put(Db.ItemsTable.COLUMN_NAME, item.getName()); + values.put(Db.ItemsTable.COLUMN_DESCRIPTION, item.getDescription()); + ret += db.update(Db.ItemsTable.TABLE_NAME, values, String.format("%s = ?", + Db.ItemsTable.COLUMN_ID), + new String[]{String.valueOf(item.getId())}); + } + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + + private int updateLogLocale(int id, String newLocale) { + int ret = 0; + SQLiteDatabase db = null; + ContentValues values; + + try { + db = getWriteDB(); + db.beginTransaction(); + + values = new ContentValues(); + values.put(Db.LogTable.COLUMN_LOCALE, newLocale); + + ret = db.update(Db.LogTable.TABLE_NAME, values, String.format("%s = ?", + Db.ItemsTable.COLUMN_ID), + new String[]{String.valueOf(id)}); + + db.setTransactionSuccessful(); + } catch (SQLiteException e) { + //TODO write to log here + + } catch (Exception e) { + //TODO write to log here + } finally { + if (db != null) { + db.endTransaction(); + db.close(); + } + } + return ret; + } + //endregion + + /////////////////////// TASK CLASSES ////////////////// + + //region Task classes + + // MY LIST CLASS + private class MyListTask implements Runnable { + int currentCommand; + int priority; + + + public MyListTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + } + handler.sendMessage(message); + } + } + // ITEM CLASS + private class ItemTask implements Runnable { + int currentCommand; + int priority; + int setId; + int itemId; + Item item; + String query; + + public ItemTask(int command, int priority) { + this.currentCommand = command; + this.setId = -1; + this.itemId = -1; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.ITEM_READ: + message.obj = readItem(setId, itemId); + if (message.obj != null) + message.what = Event.ITEM_READ_COMPLETED; + else + message.what = Event.ITEM_READ_ERROR; + message.arg1 = setId; + message.arg2 = itemId; + break; + + case Command.ITEM_DELETE_FROM_SET : + message.arg1 = updateSetItemDeleted(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_FROM_SET_DELETED; + else + message.what = Event.ITEM_FROM_SET_ERROR; + break; + + case Command.ITEM_PENDING_REMOVAL: + message.arg1 = updateSetItemPendingRemoval(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_PENDING_REMOVAL_COMPLETED; + else + message.what = Event.ITEM_PENDING_REMOVAL_ERROR; + message.arg1 = setId; + message.arg2 = itemId; + break; + + case Command.ITEM_REMOVAL_CANCEL: + message.arg1 = updateSetItemPendingRemoval(setId, itemId, false); + if (message.arg1 > 0) + message.what = Event.ITEM_REMOVAL_CANCELED; + else + message.what = Event.ITEM_REMOVAL_CANCEL_ERROR; + message.arg1 = setId; + message.arg2 = itemId; + break; + + case Command.ITEM_INSERT : + if (insertItem(item) > 0) { + if (insertSetItem(setId, item.getId(), item.isUserDefined()) > 0) { + updateSetActiveQty(setId, 1); + message.what = Event.ITEM_INSERTED; + message.arg1 = setId; + message.arg2 = item.getId(); + } + } else { + message.what = Event.ITEM_INSERT_ERROR; + } + break; + + case Command.ITEM_PACK : + message.arg1 = updateSetItemPacked(setId, itemId, true); + if (message.arg1 > 0) + message.what = Event.ITEM_PACKED; + else + message.what = Event.ITEM_PACK_ERROR; + message.arg1 = setId; + message.arg2 = itemId; + break; + + case Command.ITEM_UNPACK : + message.arg1 = updateSetItemPacked(setId, itemId, false); + if (message.arg1 > 0) + message.what = Event.ITEM_UNPACKED; + else + message.what = Event.ITEM_UNPACK_ERROR; + message.arg1 = setId; + message.arg2 = itemId; + break; + + case Command.ITEM_GET_CATEGORIES : + Hashtable res = readCategories(); + if (res.isEmpty()) + message.what = Event.ITEM_CATEGORY_LOAD_ERROR; + else { + message.what = Event.ITEM_CATEGORY_LOAD_COMPLETED; + message.obj = res; + } + break; + + case Command.ITEM_GET_LIKE : + List itemsLike = readItemsLike(query, setId); + if (itemsLike.isEmpty()) { + message.what = Event.ITEM_LIKE_LOAD_ERROR; + } else { + message.what = Event.ITEM_LIKE_LOAD_COMPLETED; + message.obj = itemsLike; + } + break; + } + handler.sendMessage(message); + } + } + // SET CLASS + private class SetTask implements Runnable { + int currentCommand; + int priority; + int setId; + String query; + List setsToUpdate; + List items; + + public SetTask(int command, int priority) { + this.currentCommand = command; + this.setId = -1; + this.setsToUpdate = new ArrayList<>(); + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.SET_GET_ALL: + List sets = readSets(); + if (sets.isEmpty()) + message.what = Event.SET_LOAD_ERROR; + else { + message.what = Event.SET_LOAD_COMPLETED; + message.obj = sets; + } + break; + + case Command.SET_GET_ITEMS: + items = readItems(setId, false); + if (items == null) + message.what = Event.SET_ITEMS_LOAD_ERROR; + else { + Collections.sort(items); + message.what = Event.SET_ITEMS_LOAD_COMPLETED; + message.obj = items; + message.arg1 = setId; + } + break; + + case Command.SET_GET_ITEMS_LIKE: + items = readItemsLike(query, setId, false); + if (items == null) + message.what = Event.SET_ITEMS_LIKE_ERROR; + else { + Collections.sort(items); + message.what = Event.SET_ITEMS_LIKE_COMPLETED; + message.obj = items; + message.arg1 = setId; + } + break; + + case Command.SET_REORDER: + message.arg1 = updateSetsOrder(setsToUpdate); + if (message.arg1 > 0) + message.what = Event.SET_REORDER_COMPLETED; + else + message.what = Event.SET_REORDER_ERROR; + break; + + case Command.SET_GET_PACKED_ITEMS : + items = readItems(setId, true); + if (items == null) + message.what = Event.SET_PACKED_LOAD_ERROR; + else { + Collections.sort(items); + message.what = Event.SET_PACKED_LOAD_COMPLETED; + message.obj = items; + message.arg1 = setId; + } + break; + + case Command.SET_GET_ITEMS_LIKE_PACKED: + items = readItemsLike(query, setId, true); + if (items == null) + message.what = Event.SET_ITEMS_LIKE_PACKED_ERROR; + else { + Collections.sort(items); + message.what = Event.SET_ITEMS_LIKE_PACKED_COMPLETED; + message.obj = items; + message.arg1 = setId; + } + break; + + case Command.SET_UNPACK_ITEMS : + message.arg1 = updateSetItemsPacked(setId, false); + message.arg2 = setId; + if (message.arg1 > 0) + message.what = Event.SET_UNPACK_COMPLETED; + else + message.what = Event.SET_UNPACK_ERROR; + break; + + case Command.SET_RESTORE_DEFAULT : + message.arg1 = updateSetRestoreDefault(setId); + if (message.arg1 > 0) + message.what = Event.SET_RESTORE_COMPLETED; + else + message.what = Event.SET_RESTORE_ERROR; + break; + + case Command.SET_GET_STAT: + Set set = readSet(setId); + message.arg1 = set.getActiveQty(); + message.arg2 = set.getPackedQty(); + message.what = Event.SET_GET_STAT_COMPLETED; + break; + + case Command.SET_CLEAN_PACKED: + message.arg1 = updateSetItemsCleanPacked(setId); + if (message.arg1 > -1) + message.what = Event.SET_PACKED_CLEANED; + else + message.what = Event.SET_CLEAN_PACKED_ERROR; + break; + } + handler.sendMessage(message); + } + } + // SYNC CLASS + private class SyncTask implements Runnable { + int currentCommand; + int priority; + int statusCode; + String locale; + + public SyncTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + this.locale = Locale.getDefault().getLanguage(); + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + + switch (currentCommand) { + case Command.SYNC: + try { + UpdateLog log = readLastLog(); + if (log != null) { + if (log.getLocale().equals(locale)) + message.what = Event.SYNC_COMPLETED; + else { + if (!NetworkUtil.isInternetAvailable()) { + message.what = Event.SYNC_FAILED; + } else { + Response> response0 = api.getSets(locale).execute(); + updateSetNames(response0.body()); + statusCode = response0.code(); + Response> response1 = api.getItemCategories(locale).execute(); + updateCategoryNames(response1.body()); + statusCode = response1.code(); + Response> response2 = api.getItems(locale).execute(); + updateItemNames(response2.body()); + statusCode = response2.code(); + updateLogLocale(log.getId(), locale); + message.what = Event.SYNC_COMPLETED; + } + } + + } else { // for the first time + if (!NetworkUtil.isInternetAvailable()) { + message.what = Event.SYNC_NO_CONNECTION; + } else { + Response> response0 = api.getSets(locale).execute(); + insertSets(response0.body()); + statusCode = response0.code(); + Response> response1 = api.getItemCategories(locale).execute(); + insertCategories(response1.body()); + statusCode = response1.code(); + Response> response2 = api.getItems(locale).execute(); + insertItems(response2.body()); + statusCode = response2.code(); + Response response3 = api.getTimestamp().execute(); + insertTimestamp(response3.body()); + statusCode = response3.code(); + message.what = Event.SYNC_COMPLETED; + } + } + + } catch (Exception e) { + message.what = Event.SYNC_FAILED; + } + finally { + message.arg1 = statusCode; + handler.sendMessage(message); + } + break; + + case Command.SYNC_READ_FROM_FILE: + + try { + String sets = readSetsFromFile(); + String categories = readCategoriesFromFile(); + String items = readItemsFromFile(); + + if (sets != null && categories != null && items != null) { + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + List setsFromJson = gson.fromJson(sets, type); + insertSets(setsFromJson); + type = new TypeToken>() { + }.getType(); + List categoriesFromJson = gson.fromJson(categories, type); + insertCategories(categoriesFromJson); + type = new TypeToken>() { + }.getType(); + List itemsFromJson = gson.fromJson(items, type); + insertItems(itemsFromJson); + insertTimestamp(new Timestamp(1456083374)); + message.what = Event.SYNC_COMPLETED; + } else { + message.what = Event.SYNC_FAILED; + } + } catch (Exception ex) { + Log.e("Read from file", ex.getMessage()); + message.what = Event.SYNC_FAILED; + } finally { + message.arg1 = 200; + handler.sendMessage(message); + } + break; + + case Command.SYNC_IF_NOT_EXISTS: + if (logExist()) { + message.what = Event.SYNC_COMPLETED; + } else { + try { + if (!NetworkUtil.isInternetAvailable()) { + message.what = Event.SYNC_NO_CONNECTION; + } else { + Response> response0 = api.getSets(locale).execute(); + insertSets(response0.body()); + statusCode = response0.code(); + Response> response1 = api.getItemCategories(locale).execute(); + insertCategories(response1.body()); + statusCode = response1.code(); + Response> response2 = api.getItems(locale).execute(); + insertItems(response2.body()); + statusCode = response2.code(); + Response response3 = api.getTimestamp().execute(); + insertTimestamp(response3.body()); + statusCode = response3.code(); + message.what = Event.SYNC_COMPLETED; + } + } catch (IOException e ){ + message.what = Event.SYNC_FAILED; + } finally { + message.arg1 = statusCode; + handler.sendMessage(message); + } + } + break; + } + } + } + // IMAGE PROVIDER CLASS + private class ImageProviderTask implements Runnable { + int currentCommand; + int priority; + Item item; + + + public ImageProviderTask(int command, int priority) { + this.currentCommand = command; + this.priority = priority; + } + + @Override + public void run() { + android.os.Process.setThreadPriority(priority); + Message message = Message.obtain(); + switch (currentCommand) { + + case Command.ITEM_GET_IMAGE : + try { + Bitmap bitmap = loadItemImage(item); + if (bitmap != null) { + message.what = Event.ITEM_IMAGE_LOAD_COMPLETED; + message.obj = bitmap; + } + else + message.what = Event.ITEM_IMAGE_LOAD_ERROR; + } catch (Exception e) { + message.what = Event.ITEM_IMAGE_LOAD_ERROR; + } + break; + } + handler.sendMessage(message); + } + } + //endregion + + private Bitmap loadItemImage(Item item){ + ImageDownloadHelper downloadHelper = new ImageDownloadHelper(); + Bitmap bitmap = null; + String filename = null; + + if (item != null) { + if (!TextUtils.isEmpty(item.getPhotoLocal())) + bitmap = BitmapFactory.decodeFile(item.getPhotoLocal()); + // cannot retrieve, download and save then + if (bitmap == null) { // return it + bitmap = downloadHelper.loadImage(item.getPhotoUrl()); + if (bitmap != null) { + if (downloadHelper.isExternalStorageWritable()) { + filename = downloadHelper.saveImageExternal( + downloadHelper.generateFileName(item.getPhotoUrl()), bitmap); + } else { + filename = downloadHelper.saveImageInternal( + downloadHelper.generateFileName(item.getPhotoUrl()), bitmap); + } + updateItemLocalPic(item.getId(), filename); + } + } + } + return bitmap; + } + + private String readSetsFromFile() { + String sets = null; + try { + InputStream is = App.getAppContext().getAssets().open("jsons/sets.json"); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + sets = new String(buffer, "UTF-8"); + } catch (IOException ex) { + Log.e("Reading json", ex.getMessage()); + } + return sets; + } + + private String readCategoriesFromFile() { + String categories = null; + try { + InputStream is = App.getAppContext().getAssets().open("jsons/item_categories.json"); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + categories = new String(buffer, "UTF-8"); + } catch (IOException ex) { + Log.e("Reading json", ex.getMessage()); + } + return categories; + } + + private String readItemsFromFile() { + String items = null; + try { + InputStream is = App.getAppContext().getAssets().open("jsons/items.json"); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + items = new String(buffer, "UTF-8"); + } catch (IOException ex) { + Log.e("Reading json", ex.getMessage()); + } + return items; + } +} diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/Db.java b/app/src/main/java/com/hikapro/backpack/model/dao/Db.java similarity index 83% rename from app/src/main/java/hikapro/com/backpack/model/dao/Db.java rename to app/src/main/java/com/hikapro/backpack/model/dao/Db.java index 702c3c8..cd72bfa 100644 --- a/app/src/main/java/hikapro/com/backpack/model/dao/Db.java +++ b/app/src/main/java/com/hikapro/backpack/model/dao/Db.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.dao; +package com.hikapro.backpack.model.dao; import android.content.ContentValues; import android.database.Cursor; @@ -8,12 +8,13 @@ import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; +import java.util.Locale; -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.model.entities.Timestamp; -import hikapro.com.backpack.model.entities.UpdateLog; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.model.entities.Timestamp; +import com.hikapro.backpack.model.entities.UpdateLog; /** * Created by tariel on 20/04/16. @@ -64,6 +65,7 @@ public class Db { public static final String COLUMN_PHOTO_THUMB_URL = "photo_thumb_url"; public static final String COLUMN_PHOTO_LOCAL = "photo_local"; public static final String COLUMN_PHOTO_THUMB_LOCAL = "photo_thumb_local"; + public static final String COLUMN_USER_DEFINED = "user_defined"; public static final String CREATE = @@ -76,6 +78,7 @@ public class Db { COLUMN_PHOTO_URL + " TEXT, " + COLUMN_PHOTO_THUMB_URL + " TEXT, " + COLUMN_PHOTO_LOCAL + " TEXT, " + + COLUMN_USER_DEFINED + " NUMERIC, " + COLUMN_PHOTO_THUMB_LOCAL + " TEXT" + " ); "; @@ -98,6 +101,8 @@ public class Db { values.put(COLUMN_PHOTO_THUMB_URL, item.getPhotoThumbUrl()); if (item.getPhotoLocal() != null) values.put(COLUMN_PHOTO_LOCAL, item.getPhotoLocal()); + + values.put(COLUMN_USER_DEFINED, item.isUserDefined()); /* values.put(COLUMN_PHOTO_THUMB_LOCAL, item.getName()); */ @@ -122,6 +127,18 @@ public class Db { item.setPhotoUrl(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_URL))); item.setPhotoThumbUrl(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_THUMB_URL))); item.setPhotoLocal(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_PHOTO_LOCAL))); + short buf = cursor.getShort(cursor.getColumnIndexOrThrow(COLUMN_USER_DEFINED)); + item.setUserDefined(buf != 0); + int check = cursor.getColumnIndex(SetItemsTable.COLUMN_PENDING_REMOVAL); + if (check != -1) { + buf = cursor.getShort(check); + item.setPendingRemoval(buf != 0); + } + check = cursor.getColumnIndex(SetItemsTable.COLUMN_PACKED); + if (check != -1) { + buf = cursor.getShort(check); + item.setPacked(buf != 0); + } return item; } @@ -215,11 +232,13 @@ public class Db { public static final String COLUMN_ID = "_id"; public static final String COLUMN_MODIFIED_DATETIME = "modified_datetime"; public static final String COLUMN_TIMESTAMP = "timestamp"; + public static final String COLUMN_LOCALE = "locale"; public static final String CREATE = "CREATE TABLE " + TABLE_NAME + " (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_TIMESTAMP + " INTEGER NOT NULL, " + + COLUMN_LOCALE + " TEXT, " + COLUMN_MODIFIED_DATETIME + " INTEGER NOT NULL DEFAULT current_timestamp" + " ); "; @@ -227,13 +246,16 @@ public class Db { ContentValues values = new ContentValues(); values.put(COLUMN_TIMESTAMP, timestamp.timestamp); + values.put(COLUMN_LOCALE, Locale.getDefault().getLanguage()); return values; } public static UpdateLog parseCursor(Cursor cursor) { UpdateLog log = new UpdateLog(); + log.setId(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_ID))); log.setTimestamp(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_TIMESTAMP))); log.setModifiedDatetime(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_MODIFIED_DATETIME))); + log.setLocale(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_LOCALE))); return log; } } @@ -246,14 +268,18 @@ public class Db { public static final String COLUMN_SET = "setId"; public static final String COLUMN_ITEM = "itemId"; public static final String COLUMN_DELETED = "deleted"; + public static final String COLUMN_PENDING_REMOVAL = "pending_removal"; public static final String COLUMN_PACKED = "packed"; + public static final String COLUMN_USER_DEFINED = "user_defined"; public static final String CREATE = "CREATE TABLE " + TABLE_NAME + " (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_SET + " INTEGER NOT NULL, " + COLUMN_DELETED + " NUMERIC, " + + COLUMN_PENDING_REMOVAL + " NUMERIC, " + COLUMN_PACKED + " NUMERIC, " + + COLUMN_USER_DEFINED + " NUMERIC, " + COLUMN_ITEM + " INTEGER NOT NULL" + " ); "; @@ -262,7 +288,20 @@ public class Db { values.put(COLUMN_SET, setId); values.put(COLUMN_ITEM, itemId); values.put(COLUMN_DELETED, 0); + values.put(COLUMN_PENDING_REMOVAL, 0); values.put(COLUMN_PACKED, 0); + values.put(COLUMN_USER_DEFINED, 0); + return values; + } + + public static ContentValues toContentValues(int setId, int itemId, boolean userDefined) { + ContentValues values = new ContentValues(); + values.put(COLUMN_SET, setId); + values.put(COLUMN_ITEM, itemId); + values.put(COLUMN_DELETED, 0); + values.put(COLUMN_PENDING_REMOVAL, 0); + values.put(COLUMN_PACKED, 0); + values.put(COLUMN_USER_DEFINED, userDefined); return values; } } diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/DbHelper.java b/app/src/main/java/com/hikapro/backpack/model/dao/DbHelper.java similarity index 96% rename from app/src/main/java/hikapro/com/backpack/model/dao/DbHelper.java rename to app/src/main/java/com/hikapro/backpack/model/dao/DbHelper.java index e4e6a03..0eb60f8 100644 --- a/app/src/main/java/hikapro/com/backpack/model/dao/DbHelper.java +++ b/app/src/main/java/com/hikapro/backpack/model/dao/DbHelper.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.dao; +package com.hikapro.backpack.model.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/Event.java b/app/src/main/java/com/hikapro/backpack/model/dao/Event.java similarity index 65% rename from app/src/main/java/hikapro/com/backpack/model/dao/Event.java rename to app/src/main/java/com/hikapro/backpack/model/dao/Event.java index b120747..99490a2 100644 --- a/app/src/main/java/hikapro/com/backpack/model/dao/Event.java +++ b/app/src/main/java/com/hikapro/backpack/model/dao/Event.java @@ -1,19 +1,20 @@ -package hikapro.com.backpack.model.dao; +package com.hikapro.backpack.model.dao; /** * Created by tariel on 27/04/16. */ public interface Event { - int SET_SCOPE_END = 0x13; - int ITEM_SCOPE_END = 0x27; - int MY_LIST_SCOPE_END = 0x3B; - int SET_LOAD_ERROR = -0x1; int SET_REORDER_ERROR = -0x2; int SET_ITEMS_LOAD_ERROR = -0x3; int SET_PACKED_LOAD_ERROR = -0x4; int SET_UNPACK_ERROR = -0x5; + int SET_RESTORE_ERROR = -0x6; + int SET_GET_STAT_ERROR = -0x7; + int SET_CLEAN_PACKED_ERROR = -0x8; + int SET_ITEMS_LIKE_ERROR = -0x9; + int SET_ITEMS_LIKE_PACKED_ERROR = -0xA; int SET_LOAD_COMPLETED = 0x1; @@ -21,7 +22,11 @@ public interface Event { int SET_ITEMS_LOAD_COMPLETED = 0x3; int SET_PACKED_LOAD_COMPLETED = 0x4; int SET_UNPACK_COMPLETED = 0x5; - + int SET_RESTORE_COMPLETED = 0x6; + int SET_GET_STAT_COMPLETED = 0x7; + int SET_PACKED_CLEANED = 0x8; + int SET_ITEMS_LIKE_COMPLETED = 0x9; + int SET_ITEMS_LIKE_PACKED_COMPLETED = 0xA; int ITEM_FROM_SET_ERROR = -0x14; int ITEM_INSERT_ERROR = -0x15; @@ -30,6 +35,10 @@ public interface Event { int ITEM_UNPACK_ERROR = -0x18; int ITEM_CATEGORY_LOAD_ERROR = -0x19; int ITEM_IMAGE_LOAD_ERROR = -0x1A; + int ITEM_LIKE_LOAD_ERROR = -0x1B; + int ITEM_PENDING_REMOVAL_ERROR = -0x1C; + int ITEM_REMOVAL_CANCEL_ERROR = -0x1D; + int ITEM_READ_ERROR = -0x1E; int ITEM_FROM_SET_DELETED = 0x14; int ITEM_INSERTED = 0x15; @@ -38,7 +47,10 @@ public interface Event { int ITEM_UNPACKED = 0x18; int ITEM_CATEGORY_LOAD_COMPLETED = 0x19; int ITEM_IMAGE_LOAD_COMPLETED = 0x1A; - + int ITEM_LIKE_LOAD_COMPLETED = 0x1B; + int ITEM_PENDING_REMOVAL_COMPLETED = 0x1C; + int ITEM_REMOVAL_CANCELED = 0x1D; + int ITEM_READ_COMPLETED = 0x1E; int MY_LIST_POST_ERROR = -0x28; int MY_LIST_ITEM_ADD_ERROR = -0x29; @@ -51,6 +63,7 @@ public interface Event { int MY_LIST_CLEARED = 0x2B; int SYNC_FAILED = -0x3C; + int SYNC_NO_CONNECTION = -0x3D; int SYNC_COMPLETED = 0x3C; int NOT_IMPLEMENTED = 0x50; diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/ImageDownloadHelper.java b/app/src/main/java/com/hikapro/backpack/model/dao/ImageDownloadHelper.java similarity index 53% rename from app/src/main/java/hikapro/com/backpack/model/dao/ImageDownloadHelper.java rename to app/src/main/java/com/hikapro/backpack/model/dao/ImageDownloadHelper.java index ebc2660..5a7c075 100644 --- a/app/src/main/java/hikapro/com/backpack/model/dao/ImageDownloadHelper.java +++ b/app/src/main/java/com/hikapro/backpack/model/dao/ImageDownloadHelper.java @@ -1,26 +1,29 @@ -package hikapro.com.backpack.model.dao; +package com.hikapro.backpack.model.dao; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Environment; +import android.support.v4.content.ContextCompat; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; -import hikapro.com.backpack.App; +import com.hikapro.backpack.App; /** * Created by tariel on 04/05/16. */ public class ImageDownloadHelper { - public static final String ITEMS_TAG = "items"; + public static final String IMAGE_DOWNLOAD_TAG = "Image download"; + public static final String IMAGE_DIRECTORY_NAME = "items"; private Context context; public ImageDownloadHelper() { @@ -32,10 +35,15 @@ public class ImageDownloadHelper { try { URL url = new URL(netPath); URLConnection conn = url.openConnection(); + conn.setReadTimeout(4000); bitmap = BitmapFactory.decodeStream(conn.getInputStream()); + } catch (SocketTimeoutException ex) { + bitmap = null; + Log.e(IMAGE_DOWNLOAD_TAG, " Timeout elapsed."); } catch (Exception ex) { - Log.e(ITEMS_TAG, " File cannot be downloaded"); + Log.e(IMAGE_DOWNLOAD_TAG, " File cannot be downloaded due to exception."); } + return bitmap; } @@ -62,8 +70,9 @@ public class ImageDownloadHelper { try { file = File.createTempFile(fileName, null, context.getCacheDir()); } catch (IOException e) { - Log.e(ITEMS_TAG, " Cannot obtain temp file"); + Log.e(IMAGE_DOWNLOAD_TAG, " Cannot obtain temp file"); } + return file; } @@ -77,17 +86,25 @@ public class ImageDownloadHelper { public String saveImageInternal(String filename, Bitmap bitmap) { File file = null; - FileOutputStream outputStream; + FileOutputStream outputStream = null; if (bitmap != null && !filename.isEmpty()) { file = createTempFile(filename); if (file != null) { try { outputStream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); - outputStream.flush(); - outputStream.close(); } catch (Exception e) { - Log.e(ITEMS_TAG, " File cannot be saved"); + Log.e(IMAGE_DOWNLOAD_TAG, "File cannot be saved (Internal): " + e); + } finally { + + if (outputStream != null) { + try { + outputStream.flush(); + outputStream.close(); + } catch (IOException e) { + // do nothing here + } + } } } } @@ -96,18 +113,32 @@ public class ImageDownloadHelper { public String saveImageExternal(String filename, Bitmap bitmap) { File file = null; - FileOutputStream outputStream; + FileOutputStream outputStream = null; if (isExternalStorageWritable()) { if (bitmap != null && !filename.isEmpty()) { - File directory = getDir(); - file = new File(directory.getAbsoluteFile(), filename); try { - outputStream = new FileOutputStream(file); - bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); - outputStream.flush(); - outputStream.close(); + File directory = getDir(); + if (directory != null) { + file = new File(directory.getAbsoluteFile(), filename); + outputStream = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); + } else { + Log.e(IMAGE_DOWNLOAD_TAG, "Saving failed (External): directory unavailable."); + } } catch (Exception e) { - Log.e(ITEMS_TAG, " File cannot be saved"); + Log.e(IMAGE_DOWNLOAD_TAG, "File cannot be saved (External): " + e); + + } finally { + + if (outputStream != null) { + try { + outputStream.flush(); + outputStream.close(); + + } catch (IOException e) { + // do nothing here + } + } } } } @@ -116,30 +147,47 @@ public class ImageDownloadHelper { /* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { + boolean ret; String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { - return true; - } - return false; + ret = true; + } else + ret = false; + + return ret; } /* Checks if external storage is available to at least read */ public boolean isExternalStorageReadable() { + boolean ret; String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { - return true; - } - return false; + ret = true; + } else + ret = false; + + return ret; } private File getDir() { - // Get the directory for the app's private pictures directory. - File file = new File(context.getExternalFilesDir( - Environment.DIRECTORY_PICTURES), ITEMS_TAG); - if (!file.mkdirs()) { - Log.e(ITEMS_TAG, " Directory not created"); + File ret = null; + // Get available SD cards + File[] dirs = ContextCompat.getExternalFilesDirs(context, Environment.DIRECTORY_PICTURES); + if (dirs != null) { + switch (dirs.length) { + case 1: + ret = new File(dirs[0], IMAGE_DIRECTORY_NAME); // can be emulated SD card + break; + case 2: + ret = new File(dirs[1], IMAGE_DIRECTORY_NAME);// real SD card + break; + } } - return file; + + if (!ret.mkdirs()) { + Log.e(IMAGE_DOWNLOAD_TAG, "Directory was not created or already exists."); + } + return ret; } } diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/Test.java b/app/src/main/java/com/hikapro/backpack/model/dao/Test.java similarity index 84% rename from app/src/main/java/hikapro/com/backpack/model/dao/Test.java rename to app/src/main/java/com/hikapro/backpack/model/dao/Test.java index f1464cf..e9e5484 100644 --- a/app/src/main/java/hikapro/com/backpack/model/dao/Test.java +++ b/app/src/main/java/com/hikapro/backpack/model/dao/Test.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.dao; +package com.hikapro.backpack.model.dao; /** * Created by tariel on 27/04/16. diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Category.java b/app/src/main/java/com/hikapro/backpack/model/entities/Category.java similarity index 95% rename from app/src/main/java/hikapro/com/backpack/model/entities/Category.java rename to app/src/main/java/com/hikapro/backpack/model/entities/Category.java index d4187b5..6655b82 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Category.java +++ b/app/src/main/java/com/hikapro/backpack/model/entities/Category.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.entities; +package com.hikapro.backpack.model.entities; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Item.java b/app/src/main/java/com/hikapro/backpack/model/entities/Item.java similarity index 73% rename from app/src/main/java/hikapro/com/backpack/model/entities/Item.java rename to app/src/main/java/com/hikapro/backpack/model/entities/Item.java index dfe6d59..851f595 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Item.java +++ b/app/src/main/java/com/hikapro/backpack/model/entities/Item.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.entities; +package com.hikapro.backpack.model.entities; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; @@ -36,9 +36,23 @@ public class Item implements Comparable, Serializable { private String photoLocal; + private boolean userDefined; + + private boolean pendingRemoval; + + private boolean packed; + + public boolean InList; + public Item() { } + public Item(String name, int category) { + this.id = -1; + this.name = name; + this.category = category; + } + public Item(int id, String name, int category, String description, List buyUrls, String photoUrl, String photoThumbUrl) { this.id = id; @@ -114,6 +128,30 @@ public class Item implements Comparable, Serializable { this.photoLocal = photoLocal; } + public boolean isUserDefined() { + return userDefined; + } + + public void setUserDefined(boolean userDefined) { + this.userDefined = userDefined; + } + + public boolean isPendingRemoval() { + return pendingRemoval; + } + + public void setPendingRemoval(boolean pendingRemoval) { + this.pendingRemoval = pendingRemoval; + } + + public boolean isPacked() { + return packed; + } + + public void setPacked(boolean packed) { + this.packed = packed; + } + @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; @@ -136,7 +174,16 @@ public class Item implements Comparable, Serializable { @Override public int compareTo(Item another) { + /* int cmp = Integer.valueOf(category).compareTo(Integer.valueOf(another.category)); return (cmp != 0 ? cmp : name.compareTo(another.name)); + */ + int cmp = Integer.valueOf(category).compareTo(Integer.valueOf(another.category)); + if (cmp != 0) + return cmp; + else { + cmp = Boolean.valueOf(pendingRemoval).compareTo(Boolean.valueOf(another.pendingRemoval)); + return (cmp != 0 ? cmp : name.compareTo(another.name)); + } } } diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Set.java b/app/src/main/java/com/hikapro/backpack/model/entities/Set.java similarity index 98% rename from app/src/main/java/hikapro/com/backpack/model/entities/Set.java rename to app/src/main/java/com/hikapro/backpack/model/entities/Set.java index b1a4abd..1ecb529 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Set.java +++ b/app/src/main/java/com/hikapro/backpack/model/entities/Set.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.entities; +package com.hikapro.backpack.model.entities; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java b/app/src/main/java/com/hikapro/backpack/model/entities/Timestamp.java similarity index 69% rename from app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java rename to app/src/main/java/com/hikapro/backpack/model/entities/Timestamp.java index 01f5e90..bccb418 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Timestamp.java +++ b/app/src/main/java/com/hikapro/backpack/model/entities/Timestamp.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.entities; +package com.hikapro.backpack.model.entities; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; @@ -15,6 +15,10 @@ public class Timestamp { public Timestamp() { } + public Timestamp(long value) { + this.timestamp = value; + } + } diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java b/app/src/main/java/com/hikapro/backpack/model/entities/UpdateLog.java similarity index 60% rename from app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java rename to app/src/main/java/com/hikapro/backpack/model/entities/UpdateLog.java index b314744..ee33e54 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/UpdateLog.java +++ b/app/src/main/java/com/hikapro/backpack/model/entities/UpdateLog.java @@ -1,16 +1,26 @@ -package hikapro.com.backpack.model.entities; +package com.hikapro.backpack.model.entities; /** * Created by tariel on 27/04/16. */ public class UpdateLog { + private int id; private long timestamp; private long modifiedDatetime; + private String locale; public UpdateLog() { } + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + public long getTimestamp() { return timestamp; } @@ -26,4 +36,12 @@ public class UpdateLog { public void setModifiedDatetime(long modifiedDatetime) { this.modifiedDatetime = modifiedDatetime; } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } } diff --git a/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java b/app/src/main/java/com/hikapro/backpack/model/entities/Updates.java similarity index 95% rename from app/src/main/java/hikapro/com/backpack/model/entities/Updates.java rename to app/src/main/java/com/hikapro/backpack/model/entities/Updates.java index 00d2689..022a9c6 100644 --- a/app/src/main/java/hikapro/com/backpack/model/entities/Updates.java +++ b/app/src/main/java/com/hikapro/backpack/model/entities/Updates.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.model.entities; +package com.hikapro.backpack.model.entities; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; diff --git a/app/src/main/java/com/hikapro/backpack/presenter/AddPresenter.java b/app/src/main/java/com/hikapro/backpack/presenter/AddPresenter.java new file mode 100644 index 0000000..2e9c1f6 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/AddPresenter.java @@ -0,0 +1,335 @@ +package com.hikapro.backpack.presenter; + +import android.app.ActionBar; +import android.app.Activity; +import android.app.Fragment; +import android.content.Context; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.SearchView; +import android.widget.Toast; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.adapters.AddListAdapter; +import com.hikapro.backpack.presenter.adapters.helper.FlowLayout; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration2; +import com.hikapro.backpack.view.View; + +import java.lang.ref.WeakReference; + +/** + * Created by tariel on 18/05/16. + */ +public class AddPresenter implements Presenter.Add { + + private WeakReference view; + private Model.Add model; + + private AddListAdapter adapter; + private RecyclerView recycler; + private FlowLayout categoryContainer; + private ViewGroup categoryContainerMain; + private boolean isContainerAlreadyInitialised; + private Item newItem; + private Item selectedItem; + private Button currentCategoryButton; + private Button cancelButton; + private Button saveButton; + private SearchView searchView; + private boolean canSave; + + + public AddPresenter() { + this.adapter = new AddListAdapter(this); + } + + @Override + public void setView(View.Add view) { + this.view = new WeakReference<>(view); + } + + @Override + public void setModel(Model.Add model) { + this.model = model; + + } + + @Override + public Model.Add getModel() { + return model; + } + + @Override + public void onDestroy(boolean isChangingConfiguration) { + view = null; + model.onDestroy(isChangingConfiguration); + if ( !isChangingConfiguration ) { + model = null; + } + isContainerAlreadyInitialised = false; + } + + @Override + public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + android.view.View view = inflater.inflate(R.layout.fragment_add, container, false); + model.executeQuery(); + LinearLayoutManager llm = new LinearLayoutManager(getActivityContext()); + recycler = (RecyclerView) view.findViewById(R.id.add_item_recycler); + recycler.setLayoutManager(llm); + recycler.setAdapter(adapter); + recycler.setItemAnimator(new DefaultItemAnimator()); + //recycler.addItemDecoration(new DividerDecoration(getActivityContext())); + recycler.addItemDecoration(new DividerDecoration2(getActivityContext(), R.drawable.divider2, Util.dp2px(getAppContext(), 16))); + categoryContainer = (FlowLayout) view.findViewById(R.id.add_item_category_flow); + //categoryContainer.setPaddings(Util.dp2px(getAppContext(), 15), Util.dp2px(getAppContext(), 15)); // TODO check here + categoryContainerMain = (ViewGroup) view.findViewById(R.id.add_item_category_container); + categoryContainerMain.setVisibility(android.view.View.GONE); + recycler.setVisibility(android.view.View.GONE); + + Activity activity = (Activity) getActivityContext(); + if (activity != null) { + ActionBar actionBar = activity.getActionBar(); + if (actionBar != null) { + ViewGroup custActionBarView = (ViewGroup) inflater.inflate(R.layout.add_cust_actionbar, null); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setCustomView(custActionBarView); + + cancelButton = (Button) custActionBarView.findViewById(R.id.action_add_cancel); + saveButton = (Button) custActionBarView.findViewById(R.id.action_add_save); + cancelButton.setOnClickListener(new ActionBarButtonClickListener()); + saveButton.setOnClickListener(new ActionBarButtonClickListener()); + } + } + + searchView = (SearchView) view.findViewById(R.id.add_search); + searchView.setIconified(false); + searchView.setIconifiedByDefault(false); + searchView.setQueryHint("Enter a new item name"); + searchView.setSubmitButtonEnabled(false); + searchView.setBottom(20); + /* + Drawable d = getActivityContext().getResources().getDrawable( R.drawable.search_divider ); + searchView.setDividerDrawable(d);*/ + + int magId = searchView.getContext().getResources().getIdentifier("android:id/search_mag_icon", null, null); + ImageView magImage = (ImageView) searchView.findViewById(magId); + magImage.setLayoutParams(new LinearLayout.LayoutParams(0, 0)); + searchView.setMinimumHeight(40); + searchView.setOnQueryTextListener(new SearchTextListener()); +/* + int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null); + // Getting the 'search_plate' LinearLayout. + android.view.View searchPlate = searchView.findViewById(searchPlateId); + // Setting background of 'search_plate' to earlier defined drawable. + searchPlate.setBackgroundResource(R.drawable.search_divider); + +*/ + + return view; + } + + @Override + public Context getAppContext() { + try { + return getView().getAppContext(); + } catch (NullPointerException e) { + return null; + } + } + + @Override + public Context getActivityContext() { + try { + return getView().getActivityContext(); + } catch (NullPointerException e) { + return null; + } + } + + @Override + public void notifyDataSetChanged(boolean found) { + adapter.notifyDataSetChanged(); + if (found) { + categoryContainerMain.setVisibility(android.view.View.GONE); + recycler.setVisibility(android.view.View.VISIBLE); + currentCategoryButton = null; + } else { + categoryContainerMain.setVisibility(android.view.View.VISIBLE); + recycler.setVisibility(android.view.View.GONE); + initSelectCategoryContainer(); + } + } + + @Override + public void onAddItemClick(Item item) { + searchView.setQuery(item.getName(), false); + if (!item.InList) + selectedItem = item; + setSaveButtonAccess(); + } + + private void setSaveButtonAccess() { + + if (searchView.getQuery().length() > 0 && currentCategoryButton != null + || selectedItem != null) + canSave = true; + else + canSave = false; + + if (canSave) + saveButton.setTextColor(saveButton.getResources().getColor(R.color.save_green)); + else + saveButton.setTextColor(saveButton.getResources().getColor(R.color.white)); + } + + private boolean checkUserInput() { + if (!canSave) { + if (searchView.getQuery().length() == 0) + Toast.makeText(getActivityContext(), "Please, enter a name", Toast.LENGTH_SHORT).show(); + else if (categoryContainerMain.getVisibility() == android.view.View.VISIBLE) + Toast.makeText(getActivityContext(), "Please, select a category", Toast.LENGTH_SHORT).show(); + else + Toast.makeText(getActivityContext(), "Already in List!", Toast.LENGTH_SHORT).show(); + return false; + } + return true; + } + + private void initSelectCategoryContainer() { + Button button; + Category category; + if (! isContainerAlreadyInitialised) { + + for (int i = 0; i < model.getCategoriesCount(); ++i) { + category = model.getCategoryByPosition(i); + button = (Button) LayoutInflater.from(getActivityContext()).inflate( + R.layout.category_button, null); + button.setId(category.getId()); + String txt = category.getName(); + + button.setText(txt); + button.setOnClickListener(new CategoryButtonClickListener()); + categoryContainer.addView(button); + + if (currentCategoryButton != null) { + if (button.getId() == currentCategoryButton.getId()) { + currentCategoryButton = button; + Drawable d = currentCategoryButton.getBackground(); + PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP); + d.setColorFilter(filter); + } + } + } + setSaveButtonAccess(); + isContainerAlreadyInitialised = true; + } + } + + private View.Add getView() throws NullPointerException { + if ( view != null ) + return view.get(); + else + throw new NullPointerException("View is unavailable"); + } + + private class CategoryButtonClickListener implements android.view.View.OnClickListener { + @Override + public void onClick(android.view.View v) { + + Drawable d = v.getBackground(); + PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP); + d.setColorFilter(filter); + + if (currentCategoryButton != null) { + d = currentCategoryButton.getBackground(); + currentCategoryButton.invalidateDrawable(d); + d.clearColorFilter(); + } + + currentCategoryButton = (Button) v; + Toast.makeText(getActivityContext(), "Id " + currentCategoryButton.getId(), Toast.LENGTH_SHORT).show(); + setSaveButtonAccess(); + } + } + + private class ActionBarButtonClickListener implements android.view.View.OnClickListener { + @Override + public void onClick(android.view.View v) { + Fragment fragment = (Fragment) getView(); + switch (v.getId()) + { + case R.id.action_add_cancel : + Toast.makeText(getActivityContext(), "Cancel", Toast.LENGTH_SHORT).show(); + searchView.clearFocus(); + if (fragment != null) + fragment.getFragmentManager().popBackStack(); + break; + + case R.id.action_add_save : + if (checkUserInput()) { + if (selectedItem != null) { + model.add(selectedItem, getView().getSet().getId()); + } else { + newItem = new Item(searchView.getQuery().toString(), currentCategoryButton.getId()); + newItem.setUserDefined(true); + model.add(newItem, getView().getSet().getId()); + } + searchView.clearFocus(); + if (fragment != null) + fragment.getFragmentManager().popBackStack(); + } + break; + } + } + } + + private class SearchTextListener implements SearchView.OnQueryTextListener { + + public SearchTextListener() { + super(); + } + + @Override + public boolean onQueryTextChange(String newText) { + model.filter(newText, getView().getSet().getId()); + if (newText.isEmpty()) { + if (currentCategoryButton != null) { + Drawable d = currentCategoryButton.getBackground(); + currentCategoryButton.invalidateDrawable(d); + d.clearColorFilter(); + currentCategoryButton = null; + } + } + selectedItem = null; + setSaveButtonAccess(); + return true; + } + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/ItemDetailPresenter.java b/app/src/main/java/com/hikapro/backpack/presenter/ItemDetailPresenter.java new file mode 100644 index 0000000..5281430 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/ItemDetailPresenter.java @@ -0,0 +1,247 @@ +package com.hikapro.backpack.presenter; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.webkit.URLUtil; +import android.widget.ImageView; +import android.widget.TextView; + +import java.lang.ref.WeakReference; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.adapters.ItemDetailAdapter; +import com.hikapro.backpack.presenter.adapters.helper.OnSwipeTouchListener; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration2; +import com.hikapro.backpack.view.View; + +/** + * Created by tariel on 23/04/16. + */ +public class ItemDetailPresenter implements Presenter.ItemDetail { + + private WeakReference view; + private Model.Detail model; + private Model.Item exModel; + private ItemDetailAdapter adapter; + private Item item; + private int setId; + private ImageView itemPhoto; + private TextView itemDescription; + private TextView itemLink; + private int position; + private ViewGroup progress; + private OnSwipeTouchListener swipeTouchListener; + + public ItemDetailPresenter(Model.Item itemModel, int startPosition) { + this.adapter = new ItemDetailAdapter(this); + this.exModel = itemModel; + this.position = startPosition; + } + + @Override + public void onDestroy(boolean isChangingConfiguration) { + view = null; + model.onDestroy(isChangingConfiguration); + if ( !isChangingConfiguration ) { + model = null; + } + } + + @Override + public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + android.view.View view = inflater.inflate(R.layout.fragment_item_detail, container, false); + + itemPhoto = (ImageView) view.findViewById(R.id.item_photo); + itemDescription = (TextView) view.findViewById(R.id.item_description); + itemLink = (TextView) view.findViewById(R.id.item_link); + progress = (ViewGroup) view.findViewById(R.id.detail_progress_container); + progress.setVisibility(android.view.View.VISIBLE); + swipeTouchListener = new OnSwipeTouchListener(getActivityContext()) { + @Override + public void onSwipeLeft() { + swipeLeft(); + } + + @Override + public void onSwipeRight() { + swipeRight(); + } + }; + progress.setOnTouchListener(swipeTouchListener); + itemDescription.setOnTouchListener(swipeTouchListener); + + LinearLayoutManager llm = new LinearLayoutManager(getActivityContext()); + RecyclerView detailRecycler = (RecyclerView) view.findViewById(R.id.item_detail_recycler); + detailRecycler.setLayoutManager(llm); + detailRecycler.setAdapter(adapter); + detailRecycler.setItemAnimator(new DefaultItemAnimator()); + //detailRecycler.addItemDecoration(new DividerDecoration(getActivityContext())); + detailRecycler.addItemDecoration(new DividerDecoration2(getActivityContext(), R.drawable.divider, Util.dp2px(getAppContext(), 16))); + + detailRecycler.setHasFixedSize(true); + model.setCurrentItem(exModel.getItemByPosition(position)); + model.executeQuery(); + + Activity activity = (Activity) getActivityContext(); + if (activity != null) { + activity.invalidateOptionsMenu(); + ActionBar actionBar = activity.getActionBar(); + if (actionBar != null) { + actionBar.show(); + actionBar.setTitle(R.string.what_is_it); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayShowCustomEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); + } + } + return view; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + + } + + @Override + public void notifyDataSetChanged() { + adapter.notifyDataSetChanged(); + item = model.getCurrentItem(); + + if (!TextUtils.isEmpty(item.getDescription())) { + itemDescription.setText(item.getDescription()); + itemDescription.setVisibility(android.view.View.VISIBLE); + } else { + itemDescription.setVisibility(android.view.View.GONE); + } + + if (item.getBuyUrls() != null && !item.getBuyUrls().isEmpty()) { + itemLink.setVisibility(android.view.View.VISIBLE); + itemLink.setOnClickListener(new android.view.View.OnClickListener() { + @Override + public void onClick(android.view.View v) { + if (URLUtil.isValidUrl(item.getBuyUrls().get(0))) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(item.getBuyUrls().get(0))); + + Activity activity = (Activity) getActivityContext(); + if (activity != null) { + if (browserIntent.resolveActivity(activity.getPackageManager()) != null) + activity.startActivity(browserIntent); + } + } + } + }); + } else { + itemLink.setVisibility(android.view.View.GONE); + itemLink.setOnClickListener(null); + } + } + + @Override + public void setView(View.ItemDetail view) { + this.view = new WeakReference<>(view); + this.setId = view.getSetId(); + } + + @Override + public void setModel(Model.Detail model) { + this.model = model; + } + + @Override + public Model.Detail getModel() { + return model; + } + + @Override + public Item getCurrentItem() { + return item; + } + + @Override + public Context getAppContext() { + try { + return getView().getAppContext(); + } catch (NullPointerException e) { + return null; + } + } + @Override + public Context getActivityContext() { + try { + return getView().getActivityContext(); + } catch (NullPointerException e) { + return null; + } + } + + @Override + public void notifyPictureChanged() { + progress.setVisibility(android.view.View.GONE); + if (model.getPicture() != null) { + itemPhoto.setImageBitmap(model.getPicture()); + itemPhoto.setVisibility(android.view.View.VISIBLE); + itemPhoto.setOnTouchListener(swipeTouchListener); + } else { + itemPhoto.setImageBitmap(null); + itemPhoto.setVisibility(android.view.View.GONE); + } + + } + + private View.ItemDetail getView() throws NullPointerException { + if ( view != null ) + return view.get(); + else + throw new NullPointerException("View is unavailable"); + } + + public int getSetId() { + return setId; + } + + private boolean isValidPosition(int position) { + boolean ret; + ret = position > -1 && position < exModel.getItemsCount(); + return ret; + } + + private void swipeLeft() { + if (isValidPosition(position + 1)) { + // start progress + progress.setVisibility(android.view.View.VISIBLE); + itemPhoto.setVisibility(android.view.View.GONE); + // load data + model.setCurrentItem(exModel.getItemByPosition(++position)); + model.executeQuery(); + itemPhoto.setImageBitmap(null); + } + } + + private void swipeRight() { + if (isValidPosition(position - 1)) { + // start progress + progress.setVisibility(android.view.View.VISIBLE); + itemPhoto.setVisibility(android.view.View.GONE); + // load data + model.setCurrentItem(exModel.getItemByPosition(--position)); + model.executeQuery(); + itemPhoto.setImageBitmap(null); + } + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java b/app/src/main/java/com/hikapro/backpack/presenter/ItemListPresenter.java similarity index 66% rename from app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java rename to app/src/main/java/com/hikapro/backpack/presenter/ItemListPresenter.java index 77b98fe..2126992 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/ItemListPresenter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/ItemListPresenter.java @@ -1,8 +1,10 @@ -package hikapro.com.backpack.presenter; +package com.hikapro.backpack.presenter; import android.app.ActionBar; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -15,15 +17,17 @@ import android.widget.Toast; import java.lang.ref.WeakReference; -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.Model; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.adapters.helper.items.DividerDecoration; -import hikapro.com.backpack.presenter.adapters.ItemListAdapter; -import hikapro.com.backpack.presenter.adapters.helper.items.ItemSwipeCallback; -import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderDecoration; -import hikapro.com.backpack.view.View; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration; +import com.hikapro.backpack.presenter.adapters.ItemListAdapter; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration2; +import com.hikapro.backpack.presenter.adapters.helper.items.ItemSwipeCallback; +import com.hikapro.backpack.presenter.adapters.helper.items.StickyHeaderDecoration; +import com.hikapro.backpack.view.View; /** * Created by tariel on 20/04/16. @@ -57,9 +61,10 @@ public class ItemListPresenter implements Presenter.ItemList { model = null; } } + @Override public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - +// TODO check nn if (savedInstanceState != null) set = (Set) savedInstanceState.getSerializable(BUNDLE_SET_LIST_KEY); else @@ -72,19 +77,16 @@ public class ItemListPresenter implements Presenter.ItemList { final StickyHeaderDecoration decoration = new StickyHeaderDecoration(adapter); recycler.addItemDecoration(decoration); - recycler.addItemDecoration(new DividerDecoration(getActivityContext())); + //recycler.addItemDecoration(new DividerDecoration(getActivityContext())); + recycler.addItemDecoration(new DividerDecoration2(getActivityContext(), R.drawable.divider, Util.dp2px(getAppContext(), 16))); + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { decoration.invalidateHeaders(); } }); - ItemSwipeCallback itemSwipeCallback = new ItemSwipeCallback(0, ItemTouchHelper.LEFT, adapter, getActivityContext()); - - ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemSwipeCallback); - itemTouchHelper.attachToRecyclerView(recycler); - adapter.setUndoOn(true); - + recycler.setHasFixedSize(true); model.executeQuery(); footer = (LinearLayout) view.findViewById(R.id.item_list_footer); packedCount = (TextView) footer.findViewById(R.id.footer_packed_count); @@ -97,21 +99,18 @@ public class ItemListPresenter implements Presenter.ItemList { }); Activity activity = (Activity) getActivityContext(); if (activity != null) { + activity.invalidateOptionsMenu(); ActionBar actionBar = activity.getActionBar(); if (actionBar != null) { actionBar.show(); actionBar.setTitle(set.getName()); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayShowCustomEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); } } - /* - if ( model.getPackedQty() > 0) { - footer.setVisibility(android.view.View.VISIBLE); - String str = String.format("%s %d / %d", - getActivityContext().getResources().getString(R.string.packed), model.getPackedQty(), - model.getActiveItemsCount()); - packedCount.setText(str); - } - */ + return view; } @Override @@ -146,7 +145,7 @@ public class ItemListPresenter implements Presenter.ItemList { @Override public void setVisibility() { - if (model.getPackedQty() == model.getActiveItemsCount()) { + if (model.getPackedQty() > 0 && model.getPackedQty() == model.getActiveItemsCount()) { footer.setVisibility(android.view.View.VISIBLE); } else if ( model.getPackedQty() > 0) { footer.setVisibility(android.view.View.VISIBLE); @@ -156,19 +155,13 @@ public class ItemListPresenter implements Presenter.ItemList { } - private void showPackedItems () { getView().showPackedItems(set); } @Override - public void showDetails(int itemId) { - Item item = model.findItem(itemId); - if (item != null) - getView().showItemDetail(item); - else - showMessage(String.format("Item with Id %d is not found.", itemId)); - + public void showDetails(int position) { + getView().showItemDetail(getCurrentSet().getId(), model, position); } // process <-- @@ -179,6 +172,7 @@ public class ItemListPresenter implements Presenter.ItemList { this.view = new WeakReference<>(view); this.set = getView().getSet(); } + @Override public void setModel(Model.Item model) { this.model = model; @@ -197,6 +191,7 @@ public class ItemListPresenter implements Presenter.ItemList { return null; } } + @Override public Context getActivityContext() { try { @@ -229,4 +224,45 @@ public class ItemListPresenter implements Presenter.ItemList { public Set getCurrentSet() { return set; } + + @Override + public void unpack(int setId) { + model.unpackSet(setId); + } + + @Override + public void restore(final int setId) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivityContext(), AlertDialog.THEME_HOLO_DARK); + + builder.setMessage(R.string.dlg_restore_txt) + .setTitle(R.string.dlg_restore_header); + + builder.setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + model.restoreSet(setId); + } + }); + builder.setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + + //Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + //button.setBackgroundColor(Color.GREEN); + /* + Drawable d = button.getBackground(); + PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP); + d.setColorFilter(filter);*/ + //button = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); + //button.setBackgroundColor(Color.GRAY); + /* + d = button.getBackground(); + filter = new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_ATOP); + d.setColorFilter(filter);*/ + + + } } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/PackedListPresenter.java b/app/src/main/java/com/hikapro/backpack/presenter/PackedListPresenter.java similarity index 69% rename from app/src/main/java/hikapro/com/backpack/presenter/PackedListPresenter.java rename to app/src/main/java/com/hikapro/backpack/presenter/PackedListPresenter.java index 258c321..e21d8e2 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/PackedListPresenter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/PackedListPresenter.java @@ -1,5 +1,7 @@ -package hikapro.com.backpack.presenter; +package com.hikapro.backpack.presenter; +import android.app.ActionBar; +import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; @@ -9,15 +11,16 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.LinearLayout; import android.widget.TextView; -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.adapters.PackedListAdapter; -import hikapro.com.backpack.presenter.adapters.helper.items.DividerDecoration; -import hikapro.com.backpack.presenter.adapters.helper.items.ItemSwipeCallback; -import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderDecoration; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.adapters.PackedListAdapter; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration; +import com.hikapro.backpack.presenter.adapters.helper.items.DividerDecoration2; +import com.hikapro.backpack.presenter.adapters.helper.items.ItemSwipeCallback; +import com.hikapro.backpack.presenter.adapters.helper.items.StickyHeaderDecoration; /** * Created by tariel on 13/05/16. @@ -48,18 +51,15 @@ public class PackedListPresenter extends ItemListPresenter { final StickyHeaderDecoration decoration = new StickyHeaderDecoration(adapter); recycler.addItemDecoration(decoration); - recycler.addItemDecoration(new DividerDecoration(getActivityContext())); + //recycler.addItemDecoration(new DividerDecoration(getActivityContext())); + recycler.addItemDecoration(new DividerDecoration2(getActivityContext(), R.drawable.divider, Util.dp2px(getAppContext(), 16))); adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { decoration.invalidateHeaders(); } }); - ItemSwipeCallback itemSwipeCallback = new ItemSwipeCallback(0, ItemTouchHelper.LEFT, adapter, getActivityContext()); - - ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemSwipeCallback); - itemTouchHelper.attachToRecyclerView(recycler); - adapter.setUndoOn(true); + recycler.setHasFixedSize(true); model.executeQuery(); @@ -67,17 +67,7 @@ public class PackedListPresenter extends ItemListPresenter { packedCount = (TextView) view.findViewById(R.id.header_packed_count); unpackButton = (Button) view.findViewById(R.id.unpack_button); - if (model.getPackedQty() == model.getActiveItemsCount()) { - backToList.setVisibility(View.INVISIBLE); - packedCount.setVisibility(View.INVISIBLE); - unpackButton.setVisibility(View.VISIBLE); - } else { - backToList.setVisibility(View.VISIBLE); - packedCount.setVisibility(View.VISIBLE); - unpackButton.setVisibility(View.INVISIBLE); - - } - + setVisibility(); unpackButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -94,12 +84,20 @@ public class PackedListPresenter extends ItemListPresenter { } }); - /* - String str = String.format("%s %d / %d", - getActivityContext().getResources().getString(R.string.packed), - model.getPackedQty(), model.getActiveItemsCount()); - packedCount.setText(str); - */ + + Activity activity = (Activity) getActivityContext(); + if (activity != null) { + activity.invalidateOptionsMenu(); + ActionBar actionBar = activity.getActionBar(); + if (actionBar != null) { + actionBar.show(); + actionBar.setTitle(set.getName()); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayShowCustomEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); + } + } return view; } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java b/app/src/main/java/com/hikapro/backpack/presenter/Presenter.java similarity index 59% rename from app/src/main/java/hikapro/com/backpack/presenter/Presenter.java rename to app/src/main/java/com/hikapro/backpack/presenter/Presenter.java index fee3285..8790d13 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/Presenter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/Presenter.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter; +package com.hikapro.backpack.presenter; import android.content.Context; import android.graphics.Bitmap; @@ -7,12 +7,10 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.ViewGroup; -import hikapro.com.backpack.model.Model; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.view.recycler.DetailViewHolder; -import hikapro.com.backpack.view.recycler.ItemViewHolder; -import hikapro.com.backpack.view.recycler.SetViewHolder; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.view.View; /** * Created by tariel on 19/04/16. @@ -32,11 +30,12 @@ public interface Presenter { void showItemList(Set set); void onDestroy(boolean isChangingConfiguration); android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); - void setView(hikapro.com.backpack.view.View.SetList view); + void setView(com.hikapro.backpack.view.View.SetList view); void setModel(Model.Set model); Model.Set getModel(); void notifyDataSetChanged(); - void showMessage(String message); + void startProgress(); + void stopProgress(); //GLM_add_resources_SetList void onItemDismiss(int position); @@ -48,7 +47,7 @@ public interface Presenter { void onDestroy(boolean isChangingConfiguration); android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); - void setView(hikapro.com.backpack.view.View.ItemList view); + void setView(com.hikapro.backpack.view.View.ItemList view); void setModel(Model.Item model); Model.Item getModel(); void notifyDataSetChanged(); @@ -57,21 +56,42 @@ public interface Presenter { Set getCurrentSet(); void showMessage(String message); void onSaveInstanceState(Bundle outState); - void showDetails(int itemId); + void showDetails(int position); void filter(String query); + void unpack(int setId); + void restore(int setId); } interface ItemDetail extends Base { - void setView(hikapro.com.backpack.view.View.ItemDetail view); + void setView(com.hikapro.backpack.view.View.ItemDetail view); void setModel(Model.Detail model); Model.Detail getModel(); void notifyDataSetChanged(); - void showMessage(String message); + void notifyPictureChanged(); void onDestroy(boolean isChangingConfiguration); android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); void onSaveInstanceState(Bundle outState); Item getCurrentItem(); - void displayPicture(Bitmap bitmap); + int getSetId(); + } + + interface Share extends Base { + void setView(View.Share view); + void setModel(Model.Share model); + Model.Share getModel(); + void onDestroy(boolean isChangingConfiguration); + android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); + + } + + interface Add extends Base { + void setView(View.Add view); + void setModel(Model.Add model); + Model.Add getModel(); + void onDestroy(boolean isChangingConfiguration); + android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); + void notifyDataSetChanged(boolean found); + void onAddItemClick(Item item); } diff --git a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java b/app/src/main/java/com/hikapro/backpack/presenter/SetListPresenter.java similarity index 75% rename from app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java rename to app/src/main/java/com/hikapro/backpack/presenter/SetListPresenter.java index f355b35..482560e 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/SetListPresenter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/SetListPresenter.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter; +package com.hikapro.backpack.presenter; import android.app.Activity; import android.content.Context; @@ -9,19 +9,20 @@ import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.ViewGroup; +import android.widget.ProgressBar; import android.widget.Toast; import java.lang.ref.WeakReference; import java.util.Collections; -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.Model; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.adapters.SetListAdapter; -import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener; -import hikapro.com.backpack.presenter.adapters.helper.sets.SimpleItemTouchHelperCallback; -import hikapro.com.backpack.view.View; -import hikapro.com.backpack.view.recycler.SetViewHolder; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.adapters.SetListAdapter; +import com.hikapro.backpack.presenter.adapters.helper.sets.OnStartDragListener; +import com.hikapro.backpack.presenter.adapters.helper.sets.SimpleItemTouchHelperCallback; +import com.hikapro.backpack.view.View; +import com.hikapro.backpack.view.recycler.SetViewHolder; /** * Created by tariel on 20/04/16. @@ -31,6 +32,9 @@ public class SetListPresenter implements Presenter.SetList { private WeakReference view; private Model.Set model; private SetListAdapter adapter; + private ViewGroup progressBarContainer; + private ProgressBar progressBar; + private RecyclerView setRecycler; //GLM_add_resources_SetList @@ -72,8 +76,12 @@ public class SetListPresenter implements Presenter.SetList { public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { android.view.View view = inflater.inflate(R.layout.fragment_set_list, container, false); + + progressBarContainer = (ViewGroup) view.findViewById(R.id.set_progress_container); + progressBar = (ProgressBar) progressBarContainer.findViewById(R.id.set_progress); + LinearLayoutManager llm = new LinearLayoutManager(getActivityContext()); - RecyclerView setRecycler = (RecyclerView) view.findViewById(R.id.set_recycler); + setRecycler = (RecyclerView) view.findViewById(R.id.set_recycler); setRecycler.setLayoutManager(llm); setRecycler.setAdapter(adapter); setRecycler.setItemAnimator(new DefaultItemAnimator()); @@ -134,11 +142,6 @@ public class SetListPresenter implements Presenter.SetList { return model; } - @Override - public void showMessage(String message) { - Toast.makeText(getView().getAppContext(), message, Toast.LENGTH_SHORT).show(); - } - // other impl <-- private View.SetList getView() throws NullPointerException { @@ -148,6 +151,18 @@ public class SetListPresenter implements Presenter.SetList { throw new NullPointerException("View is unavailable"); } + @Override + public void startProgress() { + setRecycler.setVisibility(android.view.View.GONE); + progressBarContainer.setVisibility(android.view.View.VISIBLE); + } + + @Override + public void stopProgress() { + setRecycler.setVisibility(android.view.View.VISIBLE); + progressBarContainer.setVisibility(android.view.View.GONE); + } + //GLM_add_resources_SetList @Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { @@ -158,7 +173,7 @@ public class SetListPresenter implements Presenter.SetList { if (getView() != null) getView().showItemList(set); else - showMessage("There is no view in presenter"); + Toast.makeText(getActivityContext(), "There is no view in presenter", Toast.LENGTH_SHORT).show(); } public void onLongClick(SetViewHolder holder) { diff --git a/app/src/main/java/com/hikapro/backpack/presenter/SharePresenter.java b/app/src/main/java/com/hikapro/backpack/presenter/SharePresenter.java new file mode 100644 index 0000000..56fb14e --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/SharePresenter.java @@ -0,0 +1,142 @@ +package com.hikapro.backpack.presenter; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.Toast; + +import com.facebook.CallbackManager; +import com.facebook.FacebookCallback; +import com.facebook.FacebookException; +import com.facebook.FacebookSdk; +import com.facebook.appevents.AppEventsLogger; +import com.facebook.share.Sharer; +import com.facebook.share.model.ShareHashtag; +import com.facebook.share.model.ShareLinkContent; +import com.facebook.share.widget.ShareDialog; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.view.View; + +import java.lang.ref.WeakReference; + +/** + * Created by tariel on 16/05/16. + */ +public class SharePresenter implements Presenter.Share { + + private ShareDialog shareDialog; + private CallbackManager callbackManager; + + private Button facebookButton; + private Button twitterButton; + + private WeakReference view; + private Model.Share model; + + @Override + public Context getAppContext() { + try { + return getView().getAppContext(); + } catch (NullPointerException e) { + return null; + } + } + + @Override + public Context getActivityContext() { + try { + return getView().getActivityContext(); + } catch (NullPointerException e) { + return null; + } + } + + @Override + public void setView(View.Share view) { + this.view = new WeakReference<>(view); + + } + + @Override + public void setModel(Model.Share model) { + this.model = model; + } + + @Override + public Model.Share getModel() { + return model; + } + + @Override + public void onDestroy(boolean isChangingConfiguration) { + view = null; + model.onDestroy(isChangingConfiguration); + if ( !isChangingConfiguration ) { + model = null; + } + } + + @Override + public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + android.view.View v = inflater.inflate(R.layout.fragment_share, container, false); + + FacebookSdk.sdkInitialize(getAppContext()); + //AppEventsLogger.activateApp(getActivityContext()); + callbackManager = CallbackManager.Factory.create(); + shareDialog = new ShareDialog((Activity) getActivityContext()); + facebookButton = (Button) v.findViewById(R.id.facebook_share_button); + twitterButton = (Button) v.findViewById(R.id.twitter_share_button); + shareDialog.registerCallback(callbackManager, new + + FacebookCallback() { + @Override + public void onSuccess(Sharer.Result result) {} + + @Override + public void onCancel() {} + + @Override + public void onError(FacebookException error) {} + }); + facebookButton.setOnClickListener(new android.view.View.OnClickListener() { + @Override + public void onClick(android.view.View v) { + if (ShareDialog.canShow(ShareLinkContent.class)) { + + + ShareLinkContent linkContent = new ShareLinkContent.Builder() + .setContentTitle("Test Pack With Me app") + .setContentDescription("Test of facebook integration") + .setContentUrl(Uri.parse("http://developers.facebook.com/android")) + .setShareHashtag(new ShareHashtag.Builder() + .setHashtag("#PackWithMe") + .build()) + .build(); + + shareDialog.show(linkContent); + } + } + }); + twitterButton.setOnClickListener(new android.view.View.OnClickListener() { + @Override + public void onClick(android.view.View v) { + Toast.makeText(getActivityContext(), "Not implemented!", Toast.LENGTH_SHORT).show(); + } + }); + + return v; + } + + private View.Share getView() throws NullPointerException { + if ( view != null ) + return view.get(); + else + throw new NullPointerException("View is unavailable"); + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/AddListAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/AddListAdapter.java new file mode 100644 index 0000000..8b45761 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/AddListAdapter.java @@ -0,0 +1,53 @@ +package com.hikapro.backpack.presenter.adapters; + +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.view.recycler.AddItemViewHolder; +import com.hikapro.backpack.view.recycler.ItemViewHolder; + +/** + * Created by tariel on 18/05/16. + */ +public class AddListAdapter extends RecyclerView.Adapter { + + private Presenter.Add presenter; + + public AddListAdapter(Presenter.Add presenter) { + this.presenter = presenter; + } + + @Override + public int getItemCount() { + return presenter.getModel().getItemsCount(); + } + + @Override + public void onBindViewHolder(AddItemViewHolder holder, int position) { + final Item item = presenter.getModel().getItemByPosition(position); + holder.name.setText(item.getName()); + holder.category.setText(presenter.getModel().getCategoryById(item.getCategory()).getName()); + holder.alreadyInList.setVisibility(item.InList ? View.VISIBLE : View.GONE); + holder.item.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + presenter.onAddItemClick(item); + } + }); + } + + @Override + public AddItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + AddItemViewHolder viewHolder; + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_add, + parent, false); + viewHolder = new AddItemViewHolder(v); + return viewHolder; + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/ItemDetailAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/ItemDetailAdapter.java new file mode 100644 index 0000000..bdd65f0 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/ItemDetailAdapter.java @@ -0,0 +1,179 @@ +package com.hikapro.backpack.presenter.adapters; + +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.Paint; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.LinearLayout; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipableElement; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenu; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenuItem; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenuLayout; +import com.hikapro.backpack.view.recycler.ItemViewHolder; + +/** + * Created by tariel on 23/04/16. + */ +public class ItemDetailAdapter extends RecyclerView.Adapter { + + private Presenter.ItemDetail presenter; + + public ItemDetailAdapter(Presenter.ItemDetail presenter) { + + this.presenter = presenter; + } + + @Override + public int getItemCount() { + return presenter.getModel().getCount(); + } + + @Override + public void onBindViewHolder(final ItemViewHolder holder, final int position) { + final Item item = presenter.getModel().getCurrentItem(); + holder.cb_item.setChecked(item.isPacked()); + holder.tv_text.setText(item.getName() + " " + item.getId() + " pos " + position); + holder.im_info.setVisibility(View.GONE); + if (item.isPendingRemoval()) { + holder.tv_text.setPaintFlags(holder.tv_text.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + holder.cb_item.setVisibility(View.GONE); + } else { + holder.cb_item.setVisibility(View.VISIBLE); + holder.tv_text.setPaintFlags(holder.tv_text.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); + } + + holder.cb_item.setOnClickListener(new android.view.View.OnClickListener() { + @Override + public void onClick(View v) { + if (holder.cb_item.isChecked()) { + pack(item); + } else { + unpack(item); + } + } + }); + + + final SwipeMenuLayout menuLayout = (SwipeMenuLayout)holder.menu; + SwipeMenu menu = new SwipeMenu(presenter.getActivityContext()); + + menuLayout.removeAllViews(); + + // create menu items here + if (!item.isPendingRemoval()) { + // create "leave at home" item + SwipeMenuItem leaveAtHomeItem = new SwipeMenuItem(menu.getContext()); + // set id + leaveAtHomeItem.setId(ItemListAdapter.ID_LEAVE_AT_HOME); + // set item background + leaveAtHomeItem.setBackground(R.color.colorLeaveAtHomeBtnBackground); + // set item width + leaveAtHomeItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + leaveAtHomeItem.setTitle(R.string.leave_at_home_button); + // set item title font size + leaveAtHomeItem.setTitleSize(14); + // set item title font color + leaveAtHomeItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(leaveAtHomeItem); + } else { + // create "add to bag" item + SwipeMenuItem addToBagItem = new SwipeMenuItem(menu.getContext()); + // set id + addToBagItem.setId(ItemListAdapter.ID_ADD_TO_BAG); + // set item background + addToBagItem.setBackground(R.color.colorAddToBagBtnBackground); + // set item width + addToBagItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + addToBagItem.setTitle(R.string.add_to_bag_button); + // set item title font size + addToBagItem.setTitleSize(14); + // set item title font color + addToBagItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(addToBagItem); + + // create "return to list" item + SwipeMenuItem returnToListItem = new SwipeMenuItem(menu.getContext()); + // set id + returnToListItem.setId(ItemListAdapter.ID_RETURN_TO_LIST); + // set item background + returnToListItem.setBackground(R.color.colorReturnToListBtnBackground); + // set item width + returnToListItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + returnToListItem.setTitle(R.string.return_to_list_button); + // set item title font size + returnToListItem.setTitleSize(14); + // set item title font color + returnToListItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(returnToListItem); + } + + View.OnClickListener onClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case ItemListAdapter.ID_DELETE: + // no delete + break; + case ItemListAdapter.ID_LEAVE_AT_HOME: + leave(item); + break; + case ItemListAdapter.ID_RETURN_TO_LIST: + leaveCancel(item); + break; + case ItemListAdapter.ID_ADD_TO_BAG: + pack(item); + break; + } + menuLayout.notifyOnMenuItemClick(); + } + }; + menuLayout.addMenu(menu, onClickListener); + } + + @Override + public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + ItemViewHolder viewHolder; + View content = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_element, parent, false); + SwipeMenuLayout menu = new SwipeMenuLayout(parent.getContext()); + menu.setId(R.id.menu); + SwipableElement container = new SwipableElement(parent.getContext(), content, menu); + container.setSwipeDirection(SwipableElement.DIRECTION_LEFT); + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dp2px(parent.getContext(),66)); + container.setOrientation(LinearLayout.HORIZONTAL); + container.setLayoutParams(params); + viewHolder = new ItemViewHolder(container); + return viewHolder; + } + + public void leave(Item item) { + presenter.getModel().pendingRemove(item); + } + + public void pack(Item item) { + presenter.getModel().packItem(item); + } + + public void unpack(Item item) { + presenter.getModel().unpackItem(item); + } + + public void leaveCancel(Item item) { + presenter.getModel().pendingRemoveCancel(item); + } + +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/ItemListAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/ItemListAdapter.java new file mode 100644 index 0000000..86ecd6d --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/ItemListAdapter.java @@ -0,0 +1,262 @@ +package com.hikapro.backpack.presenter.adapters; + +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.Toast; + +import com.hikapro.backpack.App; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Category; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.StickyHeaderAdapter; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipableElement; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenu; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenuItem; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenuLayout; +import com.hikapro.backpack.view.recycler.HeaderViewHolder; +import com.hikapro.backpack.view.recycler.ItemViewHolder; + +/** + * Created by tariel on 01/05/16. + */ +public class ItemListAdapter extends RecyclerView.Adapter implements StickyHeaderAdapter { + + protected static final int ID_DELETE = 1; + protected static final int ID_LEAVE_AT_HOME = 2; + protected static final int ID_ADD_TO_BAG = 3; + protected static final int ID_RETURN_TO_LIST = 4; + + protected Presenter.ItemList presenter; + protected boolean checkAll; + + public ItemListAdapter(Presenter.ItemList presenter) { + this.presenter = presenter; + } + + @Override + public int getItemCount() { + int res = presenter.getModel().getItemsCount(); + return res; + } + + @Override + public long getItemId(int position) { + long ret = presenter.getModel().getItemId(position); + return ret; + } + + @Override + public void onBindViewHolder(final ItemViewHolder holder, final int position) { + + final Item item = presenter.getModel().getItemByPosition(position); + holder.cb_item.setChecked(checkAll); + holder.tv_text.setText(item.getName() + " " + item.getId() + " pos " + position); + //holder.tv_text.setTypeface(mainFace); + if (item.isPendingRemoval()) { + holder.tv_text.setPaintFlags(holder.tv_text.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + holder.cb_item.setVisibility(View.GONE); + } else { + holder.cb_item.setVisibility(View.VISIBLE); + holder.tv_text.setPaintFlags(holder.tv_text.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); + } + + holder.cb_item.setOnClickListener(new android.view.View.OnClickListener() { + @Override + public void onClick(View v) { + if (holder.cb_item.isChecked()) { + pack(item); + } else { + unpack(item); + } + } + }); + holder.im_info.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + presenter.showDetails(position); + } + }); + + final SwipeMenuLayout menuLayout = (SwipeMenuLayout)holder.menu; + final SwipeMenu menu = new SwipeMenu(presenter.getActivityContext()); + + menuLayout.removeAllViews(); + + // create menu items here + if (!item.isPendingRemoval()) { + // create "leave at home" item + SwipeMenuItem leaveAtHomeItem = new SwipeMenuItem(menu.getContext()); + // set id + leaveAtHomeItem.setId(ID_LEAVE_AT_HOME); + // set item background + leaveAtHomeItem.setBackground(R.color.colorLeaveAtHomeBtnBackground); + // set item width + leaveAtHomeItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + leaveAtHomeItem.setTitle(R.string.leave_at_home_button); + // set item title font size + leaveAtHomeItem.setTitleSize(14); + // set item title font color + leaveAtHomeItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(leaveAtHomeItem); + } else { + // create "add to bag" item + SwipeMenuItem addToBagItem = new SwipeMenuItem(menu.getContext()); + // set id + addToBagItem.setId(ID_ADD_TO_BAG); + // set item background + addToBagItem.setBackground(R.color.colorAddToBagBtnBackground); + // set item width + addToBagItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + addToBagItem.setTitle(R.string.add_to_bag_button); + // set item title font size + addToBagItem.setTitleSize(14); + // set item title font color + addToBagItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(addToBagItem); + + // create "return to list" item + SwipeMenuItem returnToListItem = new SwipeMenuItem(menu.getContext()); + // set id + returnToListItem.setId(ID_RETURN_TO_LIST); + // set item background + returnToListItem.setBackground(R.color.colorReturnToListBtnBackground); + // set item width + returnToListItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + returnToListItem.setTitle(R.string.return_to_list_button); + // set item title font size + returnToListItem.setTitleSize(14); + // set item title font color + returnToListItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(returnToListItem); + } + + // create "delete" item + SwipeMenuItem deleteItem = new SwipeMenuItem(menu.getContext()); + // set id + deleteItem.setId(ID_DELETE); + // set item background + deleteItem.setBackground(R.color.colorDeleteBtnBackground); + // set item width + deleteItem.setWidth(Util.dp2px(presenter.getActivityContext(),120)); + // set item title + deleteItem.setTitle(R.string.delete_button); + // set item title font size + deleteItem.setTitleSize(14); + // set item title font color + deleteItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(deleteItem); + + View.OnClickListener onClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case ID_DELETE: + remove(item); + break; + case ID_LEAVE_AT_HOME: + leave(item); + break; + case ID_RETURN_TO_LIST: + leaveCancel(item); + break; + case ID_ADD_TO_BAG: + pack(item); + break; + } + menuLayout.notifyOnMenuItemClick(); + + } + }; + + menuLayout.addMenu(menu, onClickListener); + + } + + @Override + public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + ItemViewHolder viewHolder; + View content = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_element, parent, false); + SwipeMenuLayout menu = new SwipeMenuLayout(parent.getContext()); + menu.setId(R.id.menu); + SwipableElement container = new SwipableElement(parent.getContext(), content, menu); + container.setSwipeDirection(SwipableElement.DIRECTION_LEFT); + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dp2px(parent.getContext(),66)); + container.setOrientation(LinearLayout.HORIZONTAL); + container.setLayoutParams(params); + viewHolder = new ItemViewHolder(container); + return viewHolder; + } + + public void filter(String query) { + presenter.getModel().filter(query); + notifyDataSetChanged(); + } + + @Override + public long getHeaderId(int position) { + /*if (position == 0) { + return -1; + } else {*/ + return presenter.getModel().getHeaderId(position); + //} + } + + @Override + public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { + HeaderViewHolder viewHolder; + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.header_element, parent, false); + viewHolder = new HeaderViewHolder(v); + return viewHolder; + } + + @Override + public void onBindHeaderViewHolder(HeaderViewHolder holder, int position) { + Category category = presenter.getModel().getCategoryByPosition(position); + holder.id = category.getId(); + holder.title.setText(category.getName()); + holder.title.setTypeface(App.getMainFace()); + holder.title.setBackgroundColor(0x2B1E15); + } + + public void leave(Item item) { + presenter.getModel().pendingRemove(item); + } + + public void leaveCancel(Item item) { + presenter.getModel().pendingRemoveCancel(item); + } + + public void remove(Item item) { + presenter.getModel().remove(item); + } + + public void pack(Item item) { + if (!item.isPacked()) + presenter.getModel().packItem(item); + } + + public void unpack(Item item) { + if (item.isPacked()) + presenter.getModel().unpackItem(item); + } + + public void setCheckAll(boolean checkAll) { + this.checkAll = checkAll; + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/PackedListAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/PackedListAdapter.java new file mode 100644 index 0000000..935bbe6 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/PackedListAdapter.java @@ -0,0 +1,172 @@ +package com.hikapro.backpack.presenter.adapters; + +import android.graphics.Color; +import android.graphics.Paint; +import android.view.View; +import android.widget.Toast; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.presenter.adapters.helper.Util; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenu; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenuItem; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipeMenuLayout; +import com.hikapro.backpack.view.recycler.ItemViewHolder; + +/** + * Created by tariel on 12/05/16. + */ +public class PackedListAdapter extends ItemListAdapter { + + public PackedListAdapter(Presenter.ItemList presenter) { + super(presenter); + } + + @Override + public void onBindViewHolder(final ItemViewHolder holder, final int position) { + + final Item item = presenter.getModel().getItemByPosition(position); + holder.cb_item.setChecked(checkAll); + holder.tv_text.setText(item.getName() + " " + item.getId() + " pos " + position); + if (item.isPendingRemoval()) { + holder.tv_text.setPaintFlags(holder.tv_text.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + holder.cb_item.setVisibility(View.GONE); + } else { + holder.cb_item.setVisibility(View.VISIBLE); + holder.tv_text.setPaintFlags(holder.tv_text.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); + } + + holder.cb_item.setOnClickListener(new android.view.View.OnClickListener() { + @Override + public void onClick(View v) { + if (holder.cb_item.isChecked()) { + pack(item); + } else { + unpack(item); + } + } + }); + holder.im_info.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + presenter.showDetails(position); + } + }); + + final SwipeMenuLayout menuLayout = (SwipeMenuLayout)holder.menu; + SwipeMenu menu = new SwipeMenu(presenter.getActivityContext()); + + menuLayout.removeAllViews(); + + // create menu items here + if (!item.isPendingRemoval()) { + // create "leave at home" item + SwipeMenuItem leaveAtHomeItem = new SwipeMenuItem(menu.getContext()); + // set id + leaveAtHomeItem.setId(ID_LEAVE_AT_HOME); + // set item background + leaveAtHomeItem.setBackground(R.color.colorLeaveAtHomeBtnBackground); + // set item width + leaveAtHomeItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + leaveAtHomeItem.setTitle(R.string.leave_at_home_button); + // set item title font size + leaveAtHomeItem.setTitleSize(14); + // set item title font color + leaveAtHomeItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(leaveAtHomeItem); + } else { + // create "add to bag" item + SwipeMenuItem addToBagItem = new SwipeMenuItem(menu.getContext()); + // set id + addToBagItem.setId(ID_ADD_TO_BAG); + // set item background + addToBagItem.setBackground(R.color.colorAddToBagBtnBackground); + // set item width + addToBagItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + addToBagItem.setTitle(R.string.add_to_bag_button); + // set item title font size + addToBagItem.setTitleSize(14); + // set item title font color + addToBagItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(addToBagItem); + + // create "return to list" item + SwipeMenuItem returnToListItem = new SwipeMenuItem(menu.getContext()); + // set id + returnToListItem.setId(ID_RETURN_TO_LIST); + // set item background + returnToListItem.setBackground(R.color.colorReturnToListBtnBackground); + // set item width + returnToListItem.setWidth(Util.dp2px(presenter.getActivityContext(), 120)); + // set item title + returnToListItem.setTitle(R.string.return_to_list_button); + // set item title font size + returnToListItem.setTitleSize(14); + // set item title font color + returnToListItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(returnToListItem); + } + + // create "delete" item + SwipeMenuItem deleteItem = new SwipeMenuItem(menu.getContext()); + // set id + deleteItem.setId(ID_DELETE); + // set item background + deleteItem.setBackground(R.color.colorDeleteBtnBackground); + // set item width + deleteItem.setWidth(Util.dp2px(presenter.getActivityContext(),120)); + // set item title + deleteItem.setTitle(R.string.delete_button); + // set item title font size + deleteItem.setTitleSize(14); + // set item title font color + deleteItem.setTitleColor(Color.WHITE); + // add to menu + menu.addMenuItem(deleteItem); + + View.OnClickListener onClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case ID_DELETE: + remove(item); + break; + case ID_LEAVE_AT_HOME: + leave(item); + break; + case ID_RETURN_TO_LIST: + unpack(item); + break; + case ID_ADD_TO_BAG: + pack(item); + break; + } + menuLayout.notifyOnMenuItemClick(); + } + }; + + menuLayout.addMenu(menu, onClickListener); + } + + @Override + public void remove(Item item) { + if (item.isPendingRemoval()) { + presenter.getModel().unpackItem(item); + presenter.getModel().remove(item); + } + else + presenter.getModel().pendingRemove(item); + } + + @Override + public void leave(Item item) { + presenter.getModel().pendingRemove(item); + } + +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/SetListAdapter.java similarity index 85% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/SetListAdapter.java index aa78956..c0c26d1 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/SetListAdapter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/SetListAdapter.java @@ -1,14 +1,16 @@ -package hikapro.com.backpack.presenter.adapters; +package com.hikapro.backpack.presenter.adapters; +import android.graphics.Typeface; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import hikapro.com.backpack.R; -import hikapro.com.backpack.presenter.SetListPresenter; -import hikapro.com.backpack.presenter.adapters.helper.sets.ItemTouchHelperAdapter; -import hikapro.com.backpack.view.recycler.SetViewHolder; +import com.hikapro.backpack.App; +import com.hikapro.backpack.R; +import com.hikapro.backpack.presenter.SetListPresenter; +import com.hikapro.backpack.presenter.adapters.helper.sets.ItemTouchHelperAdapter; +import com.hikapro.backpack.view.recycler.SetViewHolder; /** * Created by tariel on 20/04/16. @@ -25,7 +27,7 @@ public class SetListAdapter extends RecyclerView.Adapter impleme @Override public void onBindViewHolder(final SetViewHolder holder, int position) { - final hikapro.com.backpack.model.entities.Set set = presenter.getModel().getSetByPosition(position); + final com.hikapro.backpack.model.entities.Set set = presenter.getModel().getSetByPosition(position); if (rightBracket == null || rightBracket.isEmpty()) { @@ -37,6 +39,7 @@ public class SetListAdapter extends RecyclerView.Adapter impleme } holder.textView.setText(set.getName() + " " + rightBracket); + holder.textView.setTypeface(App.getMainFace()); String info = String.format("%s %d / %d", presenter.getActivityContext().getResources().getString(R.string.packed), set.getPackedQty(), diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/AutoResizeTextView.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/AutoResizeTextView.java new file mode 100644 index 0000000..dbf43e7 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/AutoResizeTextView.java @@ -0,0 +1,296 @@ +package com.hikapro.backpack.presenter.adapters.helper; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.RectF; +import android.os.Build; +import android.text.Layout.Alignment; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.util.SparseIntArray; +import android.util.TypedValue; +import android.widget.TextView; + +public class AutoResizeTextView extends TextView { + private interface SizeTester { + /** + * + * @param suggestedSize + * Size of text to be tested + * @param availableSpace + * available space in which text must fit + * @return an integer < 0 if after applying {@code suggestedSize} to + * text, it takes less space than {@code availableSpace}, > 0 + * otherwise + */ + public int onTestSize(int suggestedSize, RectF availableSpace); + } + + private RectF mTextRect = new RectF(); + + private RectF mAvailableSpaceRect; + + private SparseIntArray mTextCachedSizes; + + private TextPaint mPaint; + + private float mMaxTextSize; + + private float mSpacingMult = 1.0f; + + private float mSpacingAdd = 0.0f; + + private float mMinTextSize = 20; + + private int mWidthLimit; + + private static final int NO_LINE_LIMIT = -1; + private int mMaxLines; + + private boolean mEnableSizeCache = true; + private boolean mInitiallized; + + public AutoResizeTextView(Context context) { + super(context); + initialize(); + } + + public AutoResizeTextView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(); + } + + public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(); + } + + private void initialize() { + mPaint = new TextPaint(getPaint()); + mMaxTextSize = getTextSize(); + mAvailableSpaceRect = new RectF(); + mTextCachedSizes = new SparseIntArray(); + if (mMaxLines == 0) { + // no value was assigned during construction + mMaxLines = NO_LINE_LIMIT; + } + mInitiallized = true; + } + + @Override + public void setText(final CharSequence text, BufferType type) { + super.setText(text, type); + adjustTextSize(text.toString()); + } + + @Override + public void setTextSize(float size) { + mMaxTextSize = size; + mTextCachedSizes.clear(); + adjustTextSize(getText().toString()); + } + + @Override + public void setMaxLines(int maxlines) { + super.setMaxLines(maxlines); + mMaxLines = maxlines; + reAdjust(); + } + + public int getMaxLines() { + return mMaxLines; + } + + @Override + public void setSingleLine() { + super.setSingleLine(); + mMaxLines = 1; + reAdjust(); + } + + @Override + public void setSingleLine(boolean singleLine) { + super.setSingleLine(singleLine); + if (singleLine) { + mMaxLines = 1; + } else { + mMaxLines = NO_LINE_LIMIT; + } + reAdjust(); + } + + @Override + public void setLines(int lines) { + super.setLines(lines); + mMaxLines = lines; + reAdjust(); + } + + @Override + public void setTextSize(int unit, float size) { + Context c = getContext(); + Resources r; + + if (c == null) + r = Resources.getSystem(); + else + r = c.getResources(); + mMaxTextSize = TypedValue.applyDimension(unit, size, + r.getDisplayMetrics()); + mTextCachedSizes.clear(); + adjustTextSize(getText().toString()); + } + + @Override + public void setLineSpacing(float add, float mult) { + super.setLineSpacing(add, mult); + mSpacingMult = mult; + mSpacingAdd = add; + } + + /** + * Set the lower text size limit and invalidate the view + * + * @param minTextSize + */ + public void setMinTextSize(float minTextSize) { + mMinTextSize = minTextSize; + reAdjust(); + } + + private void reAdjust() { + adjustTextSize(getText().toString()); + } + + private void adjustTextSize(String string) { + if (!mInitiallized) { + return; + } + int startSize = (int) mMinTextSize; + int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom() + - getCompoundPaddingTop(); + mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft() + - getCompoundPaddingRight(); + mAvailableSpaceRect.right = mWidthLimit; + mAvailableSpaceRect.bottom = heightLimit; + super.setTextSize( + TypedValue.COMPLEX_UNIT_PX, + efficientTextSizeSearch(startSize, (int) mMaxTextSize, + mSizeTester, mAvailableSpaceRect)); + } + + private final SizeTester mSizeTester = new SizeTester() { + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public int onTestSize(int suggestedSize, RectF availableSPace) { + mPaint.setTextSize(suggestedSize); + String text = getText().toString(); + boolean singleline = getMaxLines() == 1; + if (singleline) { + mTextRect.bottom = mPaint.getFontSpacing(); + mTextRect.right = mPaint.measureText(text); + } else { + StaticLayout layout = new StaticLayout(text, mPaint, + mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult, + mSpacingAdd, true); + // return early if we have more lines + if (getMaxLines() != NO_LINE_LIMIT + && layout.getLineCount() > getMaxLines()) { + return 1; + } + mTextRect.bottom = layout.getHeight(); + int maxWidth = -1; + for (int i = 0; i < layout.getLineCount(); i++) { + if (maxWidth < layout.getLineWidth(i)) { + maxWidth = (int) layout.getLineWidth(i); + } + } + mTextRect.right = maxWidth; + } + + mTextRect.offsetTo(0, 0); + if (availableSPace.contains(mTextRect)) { + // may be too small, don't worry we will find the best match + return -1; + } else { + // too big + return 1; + } + } + }; + + /** + * Enables or disables size caching, enabling it will improve performance + * where you are animating a value inside TextView. This stores the font + * size against getText().length() Be careful though while enabling it as 0 + * takes more space than 1 on some fonts and so on. + * + * @param enable + * enable font size caching + */ + public void enableSizeCache(boolean enable) { + mEnableSizeCache = enable; + mTextCachedSizes.clear(); + adjustTextSize(getText().toString()); + } + + private int efficientTextSizeSearch(int start, int end, + SizeTester sizeTester, RectF availableSpace) { + if (!mEnableSizeCache) { + return binarySearch(start, end, sizeTester, availableSpace); + } + String text = getText().toString(); + int key = text == null ? 0 : text.length(); + int size = mTextCachedSizes.get(key); + if (size != 0) { + return size; + } + size = binarySearch(start, end, sizeTester, availableSpace); + mTextCachedSizes.put(key, size); + return size; + } + + private static int binarySearch(int start, int end, SizeTester sizeTester, + RectF availableSpace) { + int lastBest = start; + int lo = start; + int hi = end - 1; + int mid = 0; + while (lo <= hi) { + mid = (lo + hi) >>> 1; + int midValCmp = sizeTester.onTestSize(mid, availableSpace); + if (midValCmp < 0) { + lastBest = lo; + lo = mid + 1; + } else if (midValCmp > 0) { + hi = mid - 1; + lastBest = hi; + } else { + return mid; + } + } + // make sure to return last best + // this is what should always be returned + return lastBest; + + } + + @Override + protected void onTextChanged(final CharSequence text, final int start, + final int before, final int after) { + super.onTextChanged(text, start, before, after); + reAdjust(); + } + + @Override + protected void onSizeChanged(int width, int height, int oldwidth, + int oldheight) { + mTextCachedSizes.clear(); + super.onSizeChanged(width, height, oldwidth, oldheight); + if (width != oldwidth || height != oldheight) { + reAdjust(); + } + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/FlowLayout.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/FlowLayout.java new file mode 100644 index 0000000..a09fe61 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/FlowLayout.java @@ -0,0 +1,110 @@ +package com.hikapro.backpack.presenter.adapters.helper; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.hikapro.backpack.R; + +/** + * Created by tariel on 22/05/16. + */ +public class FlowLayout extends ViewGroup { + + private int PADDING_X; + private int PADDING_Y; + private int mHeight; + + public FlowLayout(Context context) { + super(context); + setPaddings(0,0); + } + + public void setPaddings(int V, int H){ + PADDING_X = H; + PADDING_Y = V; + } + + protected void setPaddings(Context ctx, AttributeSet attrs){ + TypedArray a = ctx + .obtainStyledAttributes(attrs, R.styleable.FlowLayout); + String H = a.getString(R.styleable.FlowLayout_paddingX); + String V = a.getString(R.styleable.FlowLayout_paddingY); + if (H == null || V == null) + setPaddings(V == null ? 0 : Integer.parseInt(V), H == null ? 0 :Integer.parseInt(H)); + else { + setPaddings(Integer.parseInt(V), Integer.parseInt(H)); + a.recycle(); + } + + } + + public FlowLayout(Context context, AttributeSet attrs) { + super(context, attrs); + setPaddings(context,attrs); + } + + public FlowLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setPaddings(context,attrs); + } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED); + final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); + int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); + final int count = getChildCount(); + int xpos = getPaddingLeft(); + int ypos = getPaddingTop(); + int childHeightMeasureSpec; + if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + else + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + mHeight = 0; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); + final int childw = child.getMeasuredWidth(); + mHeight = Math.max(mHeight, child.getMeasuredHeight() + PADDING_Y); + if (xpos + childw > width) { + xpos = getPaddingLeft(); + ypos += mHeight; + } + xpos += childw + PADDING_X; + } + } + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { + height = ypos + mHeight; + } else if(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + if(ypos + mHeight < height) { + height = ypos + mHeight; + } + } + height += 5; // Fudge to avoid clipping bottom of last row. + setMeasuredDimension(width, height); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int width = r - l; + int xpos = getPaddingLeft(); + int ypos = getPaddingTop(); + for(int i = 0; i < getChildCount(); i++) { + final View child = getChildAt(i); + if(child.getVisibility() != GONE) { + final int childw = child.getMeasuredWidth(); + final int childh = child.getMeasuredHeight(); + if(xpos + childw > width) { + xpos = getPaddingLeft(); + ypos += mHeight; + } + child.layout(xpos, ypos, xpos + childw, ypos + childh); + xpos += childw + PADDING_X; + } + } + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/OnSwipeTouchListener.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/OnSwipeTouchListener.java new file mode 100644 index 0000000..8ac3585 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/OnSwipeTouchListener.java @@ -0,0 +1,53 @@ +package com.hikapro.backpack.presenter.adapters.helper; + +import android.content.Context; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +/** + * Created by tariel on 16/07/16. + */ +public class OnSwipeTouchListener implements View.OnTouchListener { + + private final GestureDetector gestureDetector; + + public OnSwipeTouchListener(Context context) { + gestureDetector = new GestureDetector(context, new GestureListener()); + } + + public void onSwipeLeft() { + } + + public void onSwipeRight() { + } + + public boolean onTouch(View v, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + + private final class GestureListener extends GestureDetector.SimpleOnGestureListener { + + private static final int SWIPE_DISTANCE_THRESHOLD = 100; + private static final int SWIPE_VELOCITY_THRESHOLD = 100; + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + float distanceX = e2.getX() - e1.getX(); + float distanceY = e2.getY() - e1.getY(); + if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { + if (distanceX > 0) + onSwipeRight(); + else + onSwipeLeft(); + return true; + } + return false; + } + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/Util.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/Util.java new file mode 100644 index 0000000..e12fc52 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/Util.java @@ -0,0 +1,15 @@ +package com.hikapro.backpack.presenter.adapters.helper; + +import android.content.Context; +import android.util.TypedValue; + +/** + * Created by tariel on 10/07/16. + */ +public class Util { + + public static int dp2px(Context context, int dp) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + context.getResources().getDisplayMetrics()); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DimensionCalculator.java similarity index 95% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DimensionCalculator.java index cf459ed..2bce3ed 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DimensionCalculator.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DimensionCalculator.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.graphics.Rect; import android.view.View; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DividerDecoration.java similarity index 96% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DividerDecoration.java index 7076da7..a290c5d 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/DividerDecoration.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DividerDecoration.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.content.Context; import android.content.res.TypedArray; @@ -9,6 +9,8 @@ import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; +import com.hikapro.backpack.presenter.adapters.helper.Util; + public class DividerDecoration extends RecyclerView.ItemDecoration { diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DividerDecoration2.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DividerDecoration2.java new file mode 100644 index 0000000..4240077 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/DividerDecoration2.java @@ -0,0 +1,60 @@ +package com.hikapro.backpack.presenter.adapters.helper.items; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by tariel on 19/07/16. + */ +public class DividerDecoration2 extends RecyclerView.ItemDecoration { + + private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; + private int paddingX; + + private Drawable mDivider; + + /** + * Default divider will be used + */ + public DividerDecoration2(Context context) { + final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS); + mDivider = styledAttributes.getDrawable(0); + styledAttributes.recycle(); + } + + /** + * Custom divider will be used + */ + public DividerDecoration2(Context context, int resId) { + mDivider = ContextCompat.getDrawable(context, resId); + } + + public DividerDecoration2(Context context, int resId, int paddingX) { + mDivider = ContextCompat.getDrawable(context, resId); + this.paddingX = paddingX; + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + int left = parent.getPaddingLeft(); + int right = parent.getWidth() - parent.getPaddingRight(); + + int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = parent.getChildAt(i); + + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + + int top = child.getBottom() + params.bottomMargin; + int bottom = top + mDivider.getIntrinsicHeight(); + + mDivider.setBounds(left + paddingX, top, right - paddingX, bottom); + mDivider.draw(c); + } + } +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java similarity index 99% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java index 2eab5be..b41707c 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderPositionCalculator.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.graphics.Rect; import android.support.v7.widget.LinearLayoutManager; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderProvider.java similarity index 90% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderProvider.java index d760882..fca986d 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderProvider.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderProvider.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.support.v7.widget.RecyclerView; import android.view.View; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderRenderer.java similarity index 97% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderRenderer.java index 6bde81a..3ee9adc 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderRenderer.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderRenderer.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.graphics.Canvas; import android.graphics.Rect; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderViewCache.java similarity index 97% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderViewCache.java index 118aeb3..de5f7dd 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/HeaderViewCache.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/HeaderViewCache.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.support.v4.util.LongSparseArray; import android.support.v7.widget.RecyclerView; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java similarity index 82% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java index 13bc750..d5aa47f 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemSwipeCallback.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.content.Context; import android.graphics.Canvas; @@ -10,9 +10,10 @@ import android.support.v4.content.ContextCompat; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.View; +import android.view.ViewGroup; -import hikapro.com.backpack.R; -import hikapro.com.backpack.presenter.adapters.ItemListAdapter; +import com.hikapro.backpack.R; +import com.hikapro.backpack.presenter.adapters.ItemListAdapter; /** * Created by tariel on 02/05/16. @@ -21,6 +22,7 @@ public class ItemSwipeCallback extends ItemTouchHelper.SimpleCallback { // we want to cache these and not allocate anything repeatedly in the onChildDraw method Drawable background; + ViewGroup viewGroup; Drawable xMark; int xMarkMargin; boolean initiated; @@ -50,22 +52,23 @@ public class ItemSwipeCallback extends ItemTouchHelper.SimpleCallback { @Override public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int position = viewHolder.getAdapterPosition(); + /* if (adapter.isUndoOn() && adapter.isPendingRemoval(position)) { return 0; - } + }*/ return super.getSwipeDirs(recyclerView, viewHolder); } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { int swipedPosition = viewHolder.getAdapterPosition(); - +/* boolean undoOn = adapter.isUndoOn(); if (undoOn) { adapter.pendingRemoval(swipedPosition); } else { adapter.remove(swipedPosition); - } + }*/ } @Override @@ -82,11 +85,21 @@ public class ItemSwipeCallback extends ItemTouchHelper.SimpleCallback { init(); } + if (-(c.getWidth() * 0.6) > dX) { + this.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, false); + return; + } + // draw red background - background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom()); - background.draw(c); + //viewGroup.setLeft(itemView.getRight() + (int) dX); + //if ((itemView.getRight() + (int)dX) >= c.getWidth() * 0.6) { + background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom()); + background.draw(c); + //viewGroup.draw(c); + //} // draw x mark + int itemHeight = itemView.getBottom() - itemView.getTop(); int intrinsicWidth = xMark.getIntrinsicWidth(); int intrinsicHeight = xMark.getIntrinsicWidth(); diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java similarity index 98% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java index 784595e..33f5d27 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemSwipeDecoration.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.graphics.Canvas; import android.graphics.Color; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java similarity index 88% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java index 2d6547d..ebbe047 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/ItemVisibilityAdapter.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; /** * Created by tariel on 30/04/16. diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java similarity index 85% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java index f21ef78..58409dd 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/StickyHeaderAdapter.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java similarity index 99% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java index 074b954..883257a 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/StickyHeaderDecoration.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.items; +package com.hikapro.backpack.presenter.adapters.helper.items; import android.graphics.Canvas; import android.graphics.Rect; diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipableElement.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipableElement.java new file mode 100644 index 0000000..bf9f687 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipableElement.java @@ -0,0 +1,327 @@ +package com.hikapro.backpack.presenter.adapters.helper.items.swipe2; + +import android.content.Context; +import android.graphics.Rect; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v4.widget.ScrollerCompat; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.GestureDetector.OnGestureListener; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.Interpolator; +import android.widget.LinearLayout; + +/** + * Created by tariel on 18/06/16. + */ +public class SwipableElement extends LinearLayout { + + public static final int DIRECTION_LEFT = 1; + public static final int DIRECTION_RIGHT = -1; + + private static final int CONTENT_VIEW_ID = 1; + private static final int MENU_VIEW_ID = 2; + + private static final int STATE_CLOSE = 0; + private static final int STATE_OPEN = 1; + private int mSwipeDirection; + + private View mContentView; + private SwipeMenuLayout mMenuView; + private int mDownX; + private int state = STATE_CLOSE; + private GestureDetectorCompat mGestureDetector; + private OnGestureListener mGestureListener; + private boolean isFling; + private int MIN_FLING = dp2px(15); + private int MAX_VELOCITY_X = -dp2px(500); + private ScrollerCompat mOpenScroller; + private ScrollerCompat mCloseScroller; + private int mBaseX; + private Interpolator mCloseInterpolator; + private Interpolator mOpenInterpolator; + + private boolean mSwipeEnable = true; + + public SwipableElement(Context context, View contentView, SwipeMenuLayout menuView) { + this(context, contentView, menuView, null, null); + } + + public SwipableElement(Context context, View contentView, SwipeMenuLayout menuView, + Interpolator closeInterpolator, Interpolator openInterpolator) { + super(context); + + mCloseInterpolator = closeInterpolator; + mOpenInterpolator = openInterpolator; + mContentView = contentView; + mMenuView = menuView; + menuView.setSwipableElement(this); + + doSomething(); + } + + private SwipableElement(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private SwipableElement(Context context) { + super(context); + } + + + public void setSwipeDirection(int swipeDirection) { + mSwipeDirection = swipeDirection; + } + + private void doSomething() { + setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 56)); + mGestureListener = new SimpleOnGestureListener() { + @Override + public boolean onDown(MotionEvent e) { + isFling = false; + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, + float velocityX, float velocityY) { + if (e1 != null && e2 != null) { + if (Math.abs(e1.getX() - e2.getX()) > MIN_FLING + && velocityX < MAX_VELOCITY_X) { + isFling = true; + } + } + return super.onFling(e1, e2, velocityX, velocityY); + } + }; + mGestureDetector = new GestureDetectorCompat(getContext(), mGestureListener); + + if (mCloseInterpolator != null) { + mCloseScroller = ScrollerCompat.create(getContext(), + mCloseInterpolator); + } else { + mCloseScroller = ScrollerCompat.create(getContext()); + } + if (mOpenInterpolator != null) { + mOpenScroller = ScrollerCompat.create(getContext(), + mOpenInterpolator); + } else { + mOpenScroller = ScrollerCompat.create(getContext()); + } + + LayoutParams contentParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + mContentView.setLayoutParams(contentParams); + if (mContentView.getId() < 1) { + mContentView.setId(CONTENT_VIEW_ID); + } + if (mMenuView.getId() < 1) + mMenuView.setId(MENU_VIEW_ID); + mMenuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + + addView(mContentView); + addView(mMenuView); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + } + + public boolean onSwipe(MotionEvent event) { + mGestureDetector.onTouchEvent(event); + switch (event.getAction()) { + + case MotionEvent.ACTION_DOWN: + mDownX = (int) event.getX(); + isFling = false; + break; + + case MotionEvent.ACTION_MOVE: + int dis = (int) (mDownX - event.getX()); + if (state == STATE_OPEN) { + dis += mMenuView.getWidth() * mSwipeDirection; + } + swipe(dis); + break; + + case MotionEvent.ACTION_UP: + if ((isFling || Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 4)) && + Math.signum(mDownX - event.getX()) == mSwipeDirection) { + smoothOpenMenu(); + } else { + smoothCloseMenu(); + return false; + } + break; + } + return true; + } + + public boolean isOpen() { + return state == STATE_OPEN; + } + + private void swipe(int dis) { + if (!mSwipeEnable){ + return ; + } + if (Math.signum(dis) != mSwipeDirection) { + dis = 0; + } else if (mMenuView.getWidth() > 0 && Math.abs(dis) > mMenuView.getWidth()) { + dis = mMenuView.getWidth()*mSwipeDirection; + } + + mContentView.layout(-dis, mContentView.getTop(), + mContentView.getWidth() -dis, getMeasuredHeight()); + + if (mSwipeDirection == SwipableRecycler.DIRECTION_LEFT) { + + mMenuView.layout(mContentView.getWidth() - dis, mMenuView.getTop(), + mContentView.getWidth() + mMenuView.getWidth() - dis, + mMenuView.getBottom()); + } else { + mMenuView.layout(-mMenuView.getWidth() - dis, mMenuView.getTop(), + - dis, mMenuView.getBottom()); + } + } + + @Override + public void computeScroll() { + if (state == STATE_OPEN) { + if (mOpenScroller.computeScrollOffset()) { + swipe(mOpenScroller.getCurrX()*mSwipeDirection); + postInvalidate(); + } + } else { + if (mCloseScroller.computeScrollOffset()) { + swipe((mBaseX - mCloseScroller.getCurrX())*mSwipeDirection); + postInvalidate(); + } + } + } + + public void smoothCloseMenu() { + state = STATE_CLOSE; + if (mSwipeDirection == SwipableRecycler.DIRECTION_LEFT) { + mBaseX = -mContentView.getLeft(); + mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, 350); + } else { + mBaseX = mMenuView.getRight(); + mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, 350); + } + postInvalidate(); + } + + public void smoothOpenMenu() { + if(!mSwipeEnable){ + return ; + } + state = STATE_OPEN; + if (mSwipeDirection == SwipableRecycler.DIRECTION_LEFT) { + mOpenScroller.startScroll(-mContentView.getLeft(), 0, mMenuView.getWidth(), 0, 350); + } else { + mOpenScroller.startScroll(mContentView.getLeft(), 0, mMenuView.getWidth(), 0, 350); + } + postInvalidate(); + } + + public void closeMenu() { + if (mCloseScroller.computeScrollOffset()) { + mCloseScroller.abortAnimation(); + } + if (state == STATE_OPEN) { + state = STATE_CLOSE; + swipe(0); + } + } + + public void openMenu() { + if(!mSwipeEnable){ + return ; + } + if (state == STATE_CLOSE) { + state = STATE_OPEN; + swipe(mMenuView.getWidth() * mSwipeDirection); + } + } + + public View getContentView() { + return mContentView; + } + + public SwipeMenuLayout getMenuView() { + return mMenuView; + } + + private int dp2px(int dp) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + getContext().getResources().getDisplayMetrics()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + int childWidthMeasureSpec; + int childHeightMeasureSpec; + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + + mContentView.measure(childWidthMeasureSpec, childHeightMeasureSpec); + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + + mMenuView.measure(childWidthMeasureSpec, childHeightMeasureSpec); + + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mContentView.layout(0, 0, getMeasuredWidth(),mContentView.getMeasuredHeight()); + if (mSwipeDirection == DIRECTION_LEFT) { + mMenuView.layout(getMeasuredWidth(), 0, + getMeasuredWidth() + mMenuView.getMeasuredWidth(), + mContentView.getMeasuredHeight()); + } else { + mMenuView.layout(-mMenuView.getMeasuredWidth(), 0, + 0, mContentView.getMeasuredHeight()); + } + } + + public void setMenuHeight(int measuredHeight) { + LayoutParams params = (LayoutParams) mMenuView.getLayoutParams(); + if (params.height != measuredHeight) { + params.height = measuredHeight; + mMenuView.setLayoutParams(mMenuView.getLayoutParams()); + } + } + + public void setSwipeEnable(boolean swipeEnable){ + mSwipeEnable = swipeEnable; + } + + public boolean getSwipeEnable(){ + return mSwipeEnable; + } + + public boolean isMenuVisible() { + Rect bounds = new Rect(); + this.getHitRect(bounds); + if (mMenuView.getLocalVisibleRect(bounds)) { + return true; + } else { + return false; + } + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipableRecycler.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipableRecycler.java new file mode 100644 index 0000000..ab16488 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipableRecycler.java @@ -0,0 +1,196 @@ +package com.hikapro.backpack.presenter.adapters.helper.items.swipe2; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; + +/** + * Created by tariel on 06/07/16. + */ +public class SwipableRecycler extends RecyclerView { + + private static final int TOUCH_ACTION_START = 1; + private static final int TOUCH_ACTION_MOVE_X = 2; + private static final int TOUCH_ACTION_MOVE_Y = 3; + + public static final int DIRECTION_LEFT = 1; + public static final int DIRECTION_RIGHT = -1; + private int mDirection = 1; // swipe from right to left by default + + private int MAX_Y = 5; + private int MAX_X = 3; + private float mDownX; + private float mDownY; + private int mTouchState; + private SwipableElement lastTouchedElement; + + public SwipableRecycler(Context context) { + super(context); + init(); + } + + public SwipableRecycler(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public SwipableRecycler(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + View buf; + int action = e.getAction(); + + switch (action) { + case MotionEvent.ACTION_DOWN: + boolean handled = false; + mDownX = e.getX(); + mDownY = e.getY(); + handled = super.onInterceptTouchEvent(e); + mTouchState = TOUCH_ACTION_START; + buf = findChildViewUnder(mDownX, mDownY); + if (buf instanceof SwipableElement) { + + if (lastTouchedElement != null && lastTouchedElement.isOpen() && !inRangeOfView(lastTouchedElement.getMenuView(), e)) { + return true; + } + lastTouchedElement = (SwipableElement) buf; + lastTouchedElement.setSwipeDirection(mDirection); + } + if (lastTouchedElement != null && lastTouchedElement.isOpen() && lastTouchedElement != buf) { + handled = true; + } + if (lastTouchedElement != null) + lastTouchedElement.onSwipe(e); + return handled; + + case MotionEvent.ACTION_MOVE: + float dy = Math.abs((e.getY() - mDownY)); + float dx = Math.abs((e.getX() - mDownX)); + + if (Math.abs(dy) > MAX_Y || Math.abs(dx) > MAX_X) { + if (mTouchState == TOUCH_ACTION_START) { + if (Math.abs(dy) > MAX_Y) { + mTouchState = TOUCH_ACTION_MOVE_Y; + } else if (dx > MAX_X) { + mTouchState = TOUCH_ACTION_MOVE_X; + } + } + return true; + } + } + return super.onInterceptTouchEvent(e); + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + View buf; + int action = e.getAction(); + if (action != MotionEvent.ACTION_DOWN && lastTouchedElement == null) + return super.onTouchEvent(e); + + switch (action) { + case MotionEvent.ACTION_DOWN: + SwipableElement test = null; + mDownX = e.getX(); + mDownY = e.getY(); + mTouchState = TOUCH_ACTION_START; + + buf = findChildViewUnder(e.getX(), e.getY()); + + if (buf instanceof SwipableElement) + test = (SwipableElement) buf; + + if (lastTouchedElement != null && lastTouchedElement == test + && lastTouchedElement.isOpen()) { + mTouchState = TOUCH_ACTION_MOVE_X; + lastTouchedElement.onSwipe(e); + return true; + } + if (lastTouchedElement != null && lastTouchedElement.isOpen()) { + lastTouchedElement.smoothCloseMenu(); + lastTouchedElement = null; + // try to cancel the touch event + MotionEvent cancelEvent = MotionEvent.obtain(e); + cancelEvent.setAction(MotionEvent.ACTION_CANCEL); + super.onTouchEvent(cancelEvent); + return true; + } + if (buf instanceof SwipableElement) { + lastTouchedElement = (SwipableElement) buf; + lastTouchedElement.setSwipeDirection(mDirection); + } + if (lastTouchedElement != null) { + lastTouchedElement.onSwipe(e); + } + break; + + case MotionEvent.ACTION_MOVE: + float dy = Math.abs((e.getY() - mDownY)); + float dx = Math.abs((e.getX() - mDownX)); + + if (mTouchState == TOUCH_ACTION_MOVE_X) { + if (lastTouchedElement != null) { + lastTouchedElement.onSwipe(e); + } + e.setAction(MotionEvent.ACTION_CANCEL); + super.onTouchEvent(e); + return true; + } else if (mTouchState == TOUCH_ACTION_START) { + if (Math.abs(dy) > MAX_Y) { + mTouchState = TOUCH_ACTION_MOVE_Y; + } else if (dx > MAX_X) { + mTouchState = TOUCH_ACTION_MOVE_X; + } + } + break; + + case MotionEvent.ACTION_UP: + if (mTouchState == TOUCH_ACTION_MOVE_X) { + if (lastTouchedElement != null) { + boolean isBeforeOpen = lastTouchedElement.isOpen(); + lastTouchedElement.onSwipe(e); + boolean isAfterOpen = lastTouchedElement.isOpen(); + + if (!isAfterOpen) { + lastTouchedElement = null; + } + } + e.setAction(MotionEvent.ACTION_CANCEL); + super.onTouchEvent(e); + return true; + } + break; + } + return super.onTouchEvent(e); + } + + public static boolean inRangeOfView(View view, MotionEvent ev) { + int[] location = new int[2]; + view.getLocationOnScreen(location); + int x = location[0]; + int y = location[1]; + if (ev.getRawX() < x || ev.getRawX() > (x + view.getWidth()) || ev.getRawY() < y || ev.getRawY() > (y + view.getHeight())) { + return false; + } + return true; + } + + private void init() { + MAX_X = dp2px(MAX_X); + MAX_Y = dp2px(MAX_Y); + mTouchState = TOUCH_ACTION_START; + } + + private int dp2px(int dp) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + getContext().getResources().getDisplayMetrics()); + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenu.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenu.java new file mode 100644 index 0000000..000d5c2 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenu.java @@ -0,0 +1,49 @@ +package com.hikapro.backpack.presenter.adapters.helper.items.swipe2; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tariel on 18/06/16. + */ +public class SwipeMenu { + + private Context mContext; + private List mItems; + private int mViewType; + + public SwipeMenu(Context context) { + mContext = context; + mItems = new ArrayList(); + } + + public Context getContext() { + return mContext; + } + + public void addMenuItem(SwipeMenuItem item) { + mItems.add(item); + } + + public void removeMenuItem(SwipeMenuItem item) { + mItems.remove(item); + } + + public List getMenuItems() { + return mItems; + } + + public SwipeMenuItem getMenuItem(int index) { + return mItems.get(index); + } + + public int getViewType() { + return mViewType; + } + + public void setViewType(int viewType) { + this.mViewType = viewType; + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenuItem.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenuItem.java new file mode 100644 index 0000000..256663c --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenuItem.java @@ -0,0 +1,91 @@ +package com.hikapro.backpack.presenter.adapters.helper.items.swipe2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +/** + * Created by tariel on 18/06/16. + */ +public class SwipeMenuItem { + + private int id; + private Context mContext; + private String title; + private Drawable icon; + private Drawable background; + private int titleColor; + private int titleSize; + private int width; + + public SwipeMenuItem(Context context) { + mContext = context; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getTitleColor() { + return titleColor; + } + + public int getTitleSize() { + return titleSize; + } + + public void setTitleSize(int titleSize) { + this.titleSize = titleSize; + } + + public void setTitleColor(int titleColor) { + this.titleColor = titleColor; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setTitle(int resId) { + setTitle(mContext.getString(resId)); + } + + public Drawable getIcon() { + return icon; + } + + public void setIcon(Drawable icon) { + this.icon = icon; + } + + public void setIcon(int resId) { + this.icon = mContext.getResources().getDrawable(resId); + } + + public Drawable getBackground() { + return background; + } + + public void setBackground(Drawable background) { + this.background = background; + } + + public void setBackground(int resId) { + this.background = mContext.getResources().getDrawable(resId); + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } +} diff --git a/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenuLayout.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenuLayout.java new file mode 100644 index 0000000..9bd23c4 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/items/swipe2/SwipeMenuLayout.java @@ -0,0 +1,140 @@ +package com.hikapro.backpack.presenter.adapters.helper.items.swipe2; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.List; + +/** + * Created by tariel on 18/06/16. + */ +public class SwipeMenuLayout extends LinearLayout { + + private SwipeMenu swipeMenu; + private SwipableElement swipableElement; + + public SwipeMenuLayout(Context context) { + super(context); + } + + public SwipeMenuLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public SwipeMenuLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setSwipableElement(SwipableElement swipableElement) { + this.swipableElement = swipableElement; + } + + // adding menu with items + public void addMenu(SwipeMenu menu) { + swipeMenu = menu; + List items = menu.getMenuItems(); + int id = 0; + for (SwipeMenuItem item : items) { + addItem(item, item.getId() == 0 ? ++id : item.getId()); + } + } + + private void addItem(SwipeMenuItem item, int id) { + + LayoutParams params = new LayoutParams(item.getWidth(),LayoutParams.MATCH_PARENT); + LinearLayout parent = new LinearLayout(getContext()); + parent.setId(id); + parent.setGravity(Gravity.CENTER); + parent.setOrientation(LinearLayout.VERTICAL); + parent.setLayoutParams(params); + parent.setBackgroundDrawable(item.getBackground()); + addView(parent); + + if (item.getIcon() != null) { + parent.addView(createIcon(item)); + } + if (!TextUtils.isEmpty(item.getTitle())) { + parent.addView(createTitle(item)); + } + } + // adding menu with items + listener + public void addMenu(SwipeMenu menu, OnClickListener listener) { + swipeMenu = menu; + List items = menu.getMenuItems(); + int id = 0; + for (SwipeMenuItem item : items) { + addItem(item, item.getId() == 0 ? ++id : item.getId(), listener); + } + } + + private void addItem(SwipeMenuItem item, int id, OnClickListener listener) { + + LayoutParams params = new LayoutParams(item.getWidth(),LayoutParams.MATCH_PARENT); + LinearLayout parent = new LinearLayout(getContext()); + parent.setId(id); + parent.setGravity(Gravity.CENTER); + parent.setOrientation(LinearLayout.VERTICAL); + parent.setLayoutParams(params); + parent.setBackgroundDrawable(item.getBackground()); + parent.setOnClickListener(listener); + addView(parent); + + if (item.getIcon() != null) { + parent.addView(createIcon(item)); + } + if (!TextUtils.isEmpty(item.getTitle())) { + parent.addView(createTitle(item)); + } + } + + public void notifyOnMenuItemClick() { + if (swipableElement != null && swipableElement.isOpen()) + swipableElement.smoothCloseMenu(); + } + + private ImageView createIcon(SwipeMenuItem item) { + ImageView iv = new ImageView(getContext()); + iv.setImageDrawable(item.getIcon()); + return iv; + } + + private TextView createTitle(SwipeMenuItem item) { + TextView tv = new TextView(getContext()); + tv.setText(item.getTitle()); + tv.setGravity(Gravity.CENTER); + tv.setTextSize(item.getTitleSize()); + tv.setTextColor(item.getTitleColor()); + return tv; + } + + private View findViewAtPoint(float x, float y) { + View ret = null; + View buf; + for (int i = 0; i < this.getChildCount(); ++i) { + buf = getChildAt(i); + if (isPointInsideView(x, y, buf)) { + ret = buf; + break; + } + } + return ret; + } + + private boolean isPointInsideView(float x, float y, View view) { + int location[] = new int[2]; + view.getLocationOnScreen(location); + int viewX = location[0]; + int viewY = location[1]; + + // point is inside view bounds + return ((x > viewX && x < (viewX + view.getWidth())) && + (y > viewY && y < (viewY + view.getHeight()))); + } + +} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java similarity index 77% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java index a1377aa..480a00a 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/ItemTouchHelperAdapter.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.sets; +package com.hikapro.backpack.presenter.adapters.helper.sets; /** * Created by N551 on 25.04.2016. diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java similarity index 73% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java index 96085d0..2ffe2a9 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/ItemTouchHelperViewHolder.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.sets; +package com.hikapro.backpack.presenter.adapters.helper.sets; /** * Created by N551 on 25.04.2016. diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/OnStartDragListener.java similarity index 78% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/OnStartDragListener.java index 02770ec..a6d9725 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/OnStartDragListener.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/OnStartDragListener.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.sets; +package com.hikapro.backpack.presenter.adapters.helper.sets; import android.support.v7.widget.RecyclerView; diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java similarity index 98% rename from app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java rename to app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java index 0e70a70..6afddfb 100644 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java +++ b/app/src/main/java/com/hikapro/backpack/presenter/adapters/helper/sets/SimpleItemTouchHelperCallback.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.presenter.adapters.helper.sets; +package com.hikapro.backpack.presenter.adapters.helper.sets; import android.graphics.Canvas; import android.support.v7.widget.RecyclerView; diff --git a/app/src/main/java/hikapro/com/backpack/view/View.java b/app/src/main/java/com/hikapro/backpack/view/View.java similarity index 52% rename from app/src/main/java/hikapro/com/backpack/view/View.java rename to app/src/main/java/com/hikapro/backpack/view/View.java index 5d51876..1bb35d5 100644 --- a/app/src/main/java/hikapro/com/backpack/view/View.java +++ b/app/src/main/java/com/hikapro/backpack/view/View.java @@ -1,11 +1,12 @@ -package hikapro.com.backpack.view; +package com.hikapro.backpack.view; import android.content.Context; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.presenter.adapters.helper.sets.OnStartDragListener; /** * Created by tariel on 19/04/16. @@ -25,21 +26,33 @@ public interface View { } interface ItemList extends Base { - void showItemDetail(Item item); + void showItemDetail(int setId, Model.Item baseModel, int position); void showPackedItems(Set set); void setPresenter(Presenter.ItemList presenter); Set getSet(); } interface ItemDetail extends Base { void setPresenter(Presenter.ItemDetail presenter); - Item getItem(); + int getSetId(); + } + + interface Share extends Base { + void setPresenter(Presenter.Share presenter); + } + + interface Add extends Base { + void setPresenter(Presenter.Add presenter); + void setNewItem(Item item); + Set getSet(); } interface ActivityCallback { void startSetListFragment(); void startItemListFragment(Set set); void startPackedListFragment(Set set); - void startItemDetailFragment(Item item); + void startItemDetailFragment(int setId, Model.Item baseModel, int position); + void startShareFragment(int setId); + void startAddFragment(Set set); } } diff --git a/app/src/main/java/com/hikapro/backpack/view/fragments/AddFragment.java b/app/src/main/java/com/hikapro/backpack/view/fragments/AddFragment.java new file mode 100644 index 0000000..8d79497 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/view/fragments/AddFragment.java @@ -0,0 +1,160 @@ +package com.hikapro.backpack.view.fragments; + + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SearchView; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.Presenter; + +/** + * A simple {@link Fragment} subclass. + */ +public class AddFragment extends Fragment implements com.hikapro.backpack.view.View.Add { + + protected static final String BUNDLE_SET_KEY = "BUNDLE_SET_KEY"; + private Presenter.Add presenter; + private com.hikapro.backpack.view.View.ActivityCallback activityCallback; + + public static AddFragment construct() { + return new AddFragment(); + } + + public static AddFragment newFromSet(Set set) { + AddFragment ret = AddFragment.construct(); + Bundle args = new Bundle(); + args.putSerializable(BUNDLE_SET_KEY, set); + ret.setArguments(args); + return ret; + } + + + public AddFragment() { + // Required empty public constructor + } + + // life cycle --> + @Override + public void onAttach(Context context) { + super.onAttach(context); + try { + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) context; + } catch (ClassCastException e) { + throw new ClassCastException(context.toString() + + " must implement activityCallback"); + } + } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement activityCallback"); + } + Log.i(this.toString(), " onAttach"); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // setHasOptionsMenu(true); + Log.i(this.toString(), " onCreate"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + presenter.setView(this); + View view = presenter.onCreateView(inflater, container, savedInstanceState); + Log.i(this.toString(), " onCreateView"); + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + Log.i(this.toString(), " onActivityCreated"); + } + + @Override + public void onStart() { + super.onStart(); + Log.i(this.toString(), " onStart"); + } + @Override + public void onResume() { + super.onResume(); + Log.i(this.toString(), " onResume"); + } + @Override + public void onStop() { + super.onStop(); + Log.i(this.toString(), " onStop"); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + presenter.onDestroy(true); // TODO isChangingConfigurations + Log.i(this.toString(), " onDestroyView"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + presenter.onDestroy(false); // TODO isChangingConfigurations + Log.i(this.toString(), " onDestroy"); + } + + @Override + public void onDetach() { + super.onDetach(); + Log.i(this.toString(), " onDetach"); + } + + @Override + public Context getAppContext() { + return this.getActivity().getApplicationContext(); + } + + @Override + public Context getActivityContext() { + return this.getActivity(); + } + + @Override + public void setPresenter(Presenter.Add presenter) { + this.presenter = presenter; + } + + @Override + public void setNewItem(Item item) { + + } + + public Set getSet() { + return (Set) getArguments().getSerializable(BUNDLE_SET_KEY); + } +/* + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.menu_add, menu); + super.onCreateOptionsMenu(menu, inflater); + } + +*/ +} diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/ItemDetailFragment.java b/app/src/main/java/com/hikapro/backpack/view/fragments/ItemDetailFragment.java similarity index 81% rename from app/src/main/java/hikapro/com/backpack/view/fragments/ItemDetailFragment.java rename to app/src/main/java/com/hikapro/backpack/view/fragments/ItemDetailFragment.java index b7c3de9..71bccb6 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/ItemDetailFragment.java +++ b/app/src/main/java/com/hikapro/backpack/view/fragments/ItemDetailFragment.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.view.fragments; +package com.hikapro.backpack.view.fragments; import android.app.Activity; @@ -7,21 +7,25 @@ import android.os.Bundle; import android.app.Fragment; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.presenter.Presenter; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.presenter.Presenter; /** * A simple {@link Fragment} subclass. */ -public class ItemDetailFragment extends Fragment implements hikapro.com.backpack.view.View.ItemDetail { +public class ItemDetailFragment extends Fragment implements com.hikapro.backpack.view.View.ItemDetail { - private static final String BUNDLE_ITEM_KEY = "BUNDLE_ITEM_KEY"; + private static final String BUNDLE_SET_KEY = "BUNDLE_SET_KEY"; private Presenter.ItemDetail presenter; - private hikapro.com.backpack.view.View.ActivityCallback activityCallback; + private com.hikapro.backpack.view.View.ActivityCallback activityCallback; public ItemDetailFragment() { @@ -32,10 +36,10 @@ public class ItemDetailFragment extends Fragment implements hikapro.com.backpack return new ItemDetailFragment(); } - public static ItemDetailFragment newFromItem(Item item) { + public static ItemDetailFragment newInstance(int setId) { ItemDetailFragment ret = ItemDetailFragment.construct(); Bundle args = new Bundle(); - args.putSerializable(BUNDLE_ITEM_KEY, item); + args.putInt(BUNDLE_SET_KEY, setId); ret.setArguments(args); return ret; } @@ -45,7 +49,7 @@ public class ItemDetailFragment extends Fragment implements hikapro.com.backpack public void onAttach(Context context) { super.onAttach(context); try { - activityCallback = (hikapro.com.backpack.view.View.ActivityCallback) context; + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement activityCallback"); @@ -55,7 +59,7 @@ public class ItemDetailFragment extends Fragment implements hikapro.com.backpack public void onAttach(Activity activity) { super.onAttach(activity); try { - activityCallback = (hikapro.com.backpack.view.View.ActivityCallback) activity; + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement activityCallback"); @@ -137,9 +141,9 @@ public class ItemDetailFragment extends Fragment implements hikapro.com.backpack } @Override - public Item getItem() { + public int getSetId() { Bundle args = getArguments(); - Item item = (Item) args.getSerializable(BUNDLE_ITEM_KEY); - return item; + int setId = args.getInt(BUNDLE_SET_KEY); + return setId; } } diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java b/app/src/main/java/com/hikapro/backpack/view/fragments/ItemListFragment.java similarity index 73% rename from app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java rename to app/src/main/java/com/hikapro/backpack/view/fragments/ItemListFragment.java index ae1900d..d2fe483 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/ItemListFragment.java +++ b/app/src/main/java/com/hikapro/backpack/view/fragments/ItemListFragment.java @@ -1,10 +1,11 @@ -package hikapro.com.backpack.view.fragments; +package com.hikapro.backpack.view.fragments; import android.app.Activity; import android.app.Fragment; import android.content.Context; import android.os.Bundle; +import android.support.v4.app.NavUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -15,18 +16,18 @@ import android.view.ViewGroup; import android.widget.SearchView; import android.widget.Toast; -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.ItemListPresenter; -import hikapro.com.backpack.presenter.Presenter; +import com.hikapro.backpack.R; +import com.hikapro.backpack.model.Model; +import com.hikapro.backpack.model.entities.Item; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.Presenter; -public class ItemListFragment extends Fragment implements hikapro.com.backpack.view.View.ItemList, +public class ItemListFragment extends Fragment implements com.hikapro.backpack.view.View.ItemList, SearchView.OnQueryTextListener { protected static final String BUNDLE_SET_KEY = "BUNDLE_SET_KEY"; - private hikapro.com.backpack.view.View.ActivityCallback activityCallback; + private com.hikapro.backpack.view.View.ActivityCallback activityCallback; private Presenter.ItemList presenter; public ItemListFragment() { @@ -71,19 +72,24 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v @Override public boolean onOptionsItemSelected(MenuItem item) { boolean ret; + switch (item.getItemId()) { + case R.id.action_share : - Toast.makeText(getActivityContext(), "Share", Toast.LENGTH_SHORT).show(); + activityCallback.startShareFragment(getSet().getId()); ret = true; break; case R.id.action_unpack_my_bag : - Toast.makeText(getActivityContext(), "Unpack my bag", Toast.LENGTH_SHORT).show(); + presenter.unpack(getSet().getId()); ret = true; break; case R.id.action_restore_to_default : - Toast.makeText(getActivityContext(), "Restore to default", Toast.LENGTH_SHORT).show(); + presenter.restore(getSet().getId()); ret = true; break; + case R.id.action_add : + activityCallback.startAddFragment(getSet()); + ret = true; default: ret = super.onOptionsItemSelected(item); } @@ -95,7 +101,7 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v public void onAttach(Context context) { super.onAttach(context); try { - activityCallback = (hikapro.com.backpack.view.View.ActivityCallback) context; + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement activityCallback"); @@ -105,18 +111,18 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v public void onAttach(Activity activity) { super.onAttach(activity); try { - activityCallback = (hikapro.com.backpack.view.View.ActivityCallback) activity; + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement activityCallback"); } - Log.i(this.toString(), " onAttach"); + Log.i(this.getClass().getName(), " onAttach"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - Log.i(this.toString(), " onCreate"); + Log.i(this.getClass().getName(), " onCreate"); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -124,58 +130,59 @@ public class ItemListFragment extends Fragment implements hikapro.com.backpack.v // Inflate the layout for this fragment presenter.setView(this); View view = presenter.onCreateView(inflater, container, savedInstanceState); - Log.i(this.toString(), " onCreateView"); + Log.i(this.getClass().getName(), " onCreateView"); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Log.i(this.toString(), " onActivityCreated"); + Log.i(this.getClass().getName(), " onActivityCreated"); } @Override public void onStart() { super.onStart(); - Log.i(this.toString(), " onStart"); + Log.i(this.getClass().getName(), " onStart"); } @Override public void onResume() { super.onResume(); - Log.i(this.toString(), " onResume"); + Log.i(this.getClass().getName(), " onResume"); } @Override public void onStop() { super.onStop(); - Log.i(this.toString(), " onStop"); + Log.i(this.getClass().getName(), " onStop"); } @Override public void onDestroyView() { super.onDestroyView(); presenter.onDestroy(true); // TODO isChangingConfigurations - Log.i(this.toString(), " onDestroyView"); + Log.i(this.getClass().getName(), " onDestroyView"); } @Override public void onDestroy() { super.onDestroy(); presenter.onDestroy(false); // TODO isChangingConfigurations - Log.i(this.toString(), " onDestroy"); + Log.i(this.getClass().getName(), " onDestroy"); } @Override public void onDetach() { super.onDetach(); - Log.i(this.toString(), " onDetach"); + Log.i(this.getClass().getName(), " onDetach"); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); presenter.onSaveInstanceState(outState); - Log.i(this.toString(), " onSaveInstanceState"); + Log.i(this.getClass().getName(), " onSaveInstanceState"); } // life cycle <-- + @Override - public void showItemDetail(Item item) { - activityCallback.startItemDetailFragment(item); + public void showItemDetail(int setId, Model.Item baseModel, int position) { + activityCallback.startItemDetailFragment(setId, baseModel, position); } @Override diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/PackedListFragment.java b/app/src/main/java/com/hikapro/backpack/view/fragments/PackedListFragment.java similarity index 85% rename from app/src/main/java/hikapro/com/backpack/view/fragments/PackedListFragment.java rename to app/src/main/java/com/hikapro/backpack/view/fragments/PackedListFragment.java index 69e4d18..79edefa 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/PackedListFragment.java +++ b/app/src/main/java/com/hikapro/backpack/view/fragments/PackedListFragment.java @@ -1,8 +1,8 @@ -package hikapro.com.backpack.view.fragments; +package com.hikapro.backpack.view.fragments; import android.os.Bundle; -import hikapro.com.backpack.model.entities.Set; +import com.hikapro.backpack.model.entities.Set; /** * Created by tariel on 12/05/16. diff --git a/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java b/app/src/main/java/com/hikapro/backpack/view/fragments/SetListFragment.java similarity index 84% rename from app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java rename to app/src/main/java/com/hikapro/backpack/view/fragments/SetListFragment.java index 4feaf63..4605eea 100644 --- a/app/src/main/java/hikapro/com/backpack/view/fragments/SetListFragment.java +++ b/app/src/main/java/com/hikapro/backpack/view/fragments/SetListFragment.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.view.fragments; +package com.hikapro.backpack.view.fragments; import android.app.Activity; import android.app.Fragment; @@ -10,15 +10,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.adapters.helper.sets.OnStartDragListener; +import com.hikapro.backpack.model.entities.Set; +import com.hikapro.backpack.presenter.Presenter; +import com.hikapro.backpack.presenter.adapters.helper.sets.OnStartDragListener; -public class SetListFragment extends Fragment implements hikapro.com.backpack.view.View.SetList, +public class SetListFragment extends Fragment implements com.hikapro.backpack.view.View.SetList, OnStartDragListener { private Presenter.SetList presenter; - private hikapro.com.backpack.view.View.ActivityCallback activityCallback; + private com.hikapro.backpack.view.View.ActivityCallback activityCallback; public SetListFragment() { @@ -34,7 +34,7 @@ public class SetListFragment extends Fragment implements hikapro.com.backpack.vi public void onAttach(Context context) { super.onAttach(context); try { - activityCallback = (hikapro.com.backpack.view.View.ActivityCallback) context; + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement activityCallback"); @@ -44,7 +44,7 @@ public class SetListFragment extends Fragment implements hikapro.com.backpack.vi public void onAttach(Activity activity) { super.onAttach(activity); try { - activityCallback = (hikapro.com.backpack.view.View.ActivityCallback) activity; + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement activityCallback"); @@ -113,7 +113,10 @@ public class SetListFragment extends Fragment implements hikapro.com.backpack.vi @Override public void showItemList(Set set) { - activityCallback.startItemListFragment(set); + if (set.getActiveQty() == set.getPackedQty()) + activityCallback.startPackedListFragment(set); + else + activityCallback.startItemListFragment(set); } @Override diff --git a/app/src/main/java/com/hikapro/backpack/view/fragments/ShareFragment.java b/app/src/main/java/com/hikapro/backpack/view/fragments/ShareFragment.java new file mode 100644 index 0000000..4d59cbb --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/view/fragments/ShareFragment.java @@ -0,0 +1,79 @@ +package com.hikapro.backpack.view.fragments; + + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.presenter.Presenter; + +/** + * A simple {@link Fragment} subclass. + */ +public class ShareFragment extends Fragment implements com.hikapro.backpack.view.View.Share { + + private Presenter.Share presenter; + private com.hikapro.backpack.view.View.ActivityCallback activityCallback; + + + public ShareFragment() { + // Required empty public constructor + } + + public static ShareFragment construct() { + ShareFragment ret = new ShareFragment(); + return ret; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + try { + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) context; + } catch (ClassCastException e) { + throw new ClassCastException(context.toString() + + " must implement activityCallback"); + } + } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + activityCallback = (com.hikapro.backpack.view.View.ActivityCallback) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement activityCallback"); + } + Log.i(this.toString(), "onAttach"); + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + presenter.setView(this); + View v = presenter.onCreateView(inflater, container, savedInstanceState); + return v; + } + + @Override + public void setPresenter(Presenter.Share presenter) { + this.presenter = presenter; + } + + @Override + public Context getAppContext() { + return this.getActivity().getApplicationContext(); + } + + @Override + public Context getActivityContext() { + return this.getActivity(); + } +} diff --git a/app/src/main/java/com/hikapro/backpack/view/recycler/AddItemViewHolder.java b/app/src/main/java/com/hikapro/backpack/view/recycler/AddItemViewHolder.java new file mode 100644 index 0000000..a36abe5 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/view/recycler/AddItemViewHolder.java @@ -0,0 +1,32 @@ +package com.hikapro.backpack.view.recycler; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.hikapro.backpack.R; + +/** + * Created by tariel on 18/05/16. + */ +public class AddItemViewHolder extends RecyclerView.ViewHolder { + + public TextView name; + public TextView category; + public TextView alreadyInList; + public RelativeLayout item; + + public AddItemViewHolder(View itemView) { + super(itemView); + setupViews(itemView); + } + + private void setupViews(View view) { + item = (RelativeLayout) view.findViewById(R.id.add_item_item); + name = (TextView) item.findViewById(R.id.add_item_name); + category = (TextView) item.findViewById(R.id.add_item_category); + alreadyInList = (TextView) item.findViewById(R.id.add_already_in_list); + } +} diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java b/app/src/main/java/com/hikapro/backpack/view/recycler/HeaderViewHolder.java similarity index 85% rename from app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java rename to app/src/main/java/com/hikapro/backpack/view/recycler/HeaderViewHolder.java index 95de0c9..9a12bf5 100644 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/HeaderViewHolder.java +++ b/app/src/main/java/com/hikapro/backpack/view/recycler/HeaderViewHolder.java @@ -1,10 +1,10 @@ -package hikapro.com.backpack.view.recycler; +package com.hikapro.backpack.view.recycler; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; -import hikapro.com.backpack.R; +import com.hikapro.backpack.R; /** * Created by tariel on 01/05/16. diff --git a/app/src/main/java/com/hikapro/backpack/view/recycler/ItemViewHolder.java b/app/src/main/java/com/hikapro/backpack/view/recycler/ItemViewHolder.java new file mode 100644 index 0000000..40e6a50 --- /dev/null +++ b/app/src/main/java/com/hikapro/backpack/view/recycler/ItemViewHolder.java @@ -0,0 +1,37 @@ +package com.hikapro.backpack.view.recycler; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.hikapro.backpack.R; +import com.hikapro.backpack.presenter.adapters.helper.items.swipe2.SwipableElement; + + +/** + * Created by tariel on 01/05/16. + */ +public class ItemViewHolder extends RecyclerView.ViewHolder { + + public CheckBox cb_item; + public TextView tv_text; + public ImageButton im_info; + public ViewGroup menu; + + public ItemViewHolder(View v) { + super(v); + setupViews(v); + } + + private void setupViews(View view) { + cb_item = (CheckBox) view.findViewById(R.id.item_checkbox); + tv_text = (TextView) view.findViewById(R.id.item_text); + im_info = (ImageButton) view.findViewById(R.id.item_info_button); + menu = (ViewGroup) view.findViewById(R.id.menu); + + } +} diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java b/app/src/main/java/com/hikapro/backpack/view/recycler/SetViewHolder.java similarity index 89% rename from app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java rename to app/src/main/java/com/hikapro/backpack/view/recycler/SetViewHolder.java index c54d2cd..e0bce7b 100644 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/SetViewHolder.java +++ b/app/src/main/java/com/hikapro/backpack/view/recycler/SetViewHolder.java @@ -1,4 +1,4 @@ -package hikapro.com.backpack.view.recycler; +package com.hikapro.backpack.view.recycler; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -7,8 +7,8 @@ import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; -import hikapro.com.backpack.R; -import hikapro.com.backpack.presenter.adapters.helper.sets.ItemTouchHelperViewHolder; +import com.hikapro.backpack.R; +import com.hikapro.backpack.presenter.adapters.helper.sets.ItemTouchHelperViewHolder; /** * Created by tariel on 20/04/16. diff --git a/app/src/main/java/hikapro/com/backpack/model/Api.java b/app/src/main/java/hikapro/com/backpack/model/Api.java deleted file mode 100644 index d8fe92d..0000000 --- a/app/src/main/java/hikapro/com/backpack/model/Api.java +++ /dev/null @@ -1,36 +0,0 @@ -package hikapro.com.backpack.model; - -import java.util.List; - -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; - -import hikapro.com.backpack.model.entities.Timestamp; -import hikapro.com.backpack.model.entities.Updates; -import retrofit2.Call; -import retrofit2.http.GET; -import retrofit2.http.Query; - -/** - * Created by tariel on 19/04/16. - */ -public interface Api { - - @GET("api/v1/backpack/items") - Call> getItems(); - - @GET("api/v1/backpack/item_categories") - Call> getItemCategories(); - - @GET("api/v1/backpack/sets") - Call> getSets(); - - @GET("api/v1/backpack/updates/timestamp") - Call getTimestamp(); - - @GET("api/v1/backpack/updates/all") - Call getUpdates(@Query("timestamp") long timestamp); - - -} diff --git a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java b/app/src/main/java/hikapro/com/backpack/model/DetailModel.java deleted file mode 100644 index b3f09cb..0000000 --- a/app/src/main/java/hikapro/com/backpack/model/DetailModel.java +++ /dev/null @@ -1,105 +0,0 @@ -package hikapro.com.backpack.model; - -import android.graphics.Bitmap; -import android.os.Message; - -import java.util.List; - -import hikapro.com.backpack.model.dao.Command; -import hikapro.com.backpack.model.dao.DAO; -import hikapro.com.backpack.model.dao.Event; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.Presenter; - -/** - * Created by tariel on 23/04/16. - */ -public class DetailModel implements Model.Detail { - - private Presenter.ItemDetail presenter; - private Item item; - private DAO dao; - private Bitmap pic; - - public DetailModel() { - this.dao = DAO.getInstance(); - dao.registerObserver(this); - } - - // detail --> - - @Override - public int getCount() { - return 1; - } - @Override - public Item findItem(int id) { - return item; - } - - @Override - public Bitmap getPicture() { - return pic; - } - // detail <-- - - // events --> - - @Override - public void notifyDataSetChanged() { - presenter.notifyDataSetChanged(); - } - @Override - public void onDestroy(boolean isConfigurationChanging) { - if ( !isConfigurationChanging ) { - presenter = null; - } - } - - private void sendMessage(String message) { - presenter.showMessage(message); - } - - // events <-- - - // process --> - @Override - public void executeQuery() { - - notifyDataSetChanged(); - Message command; - command = Message.obtain(); - command.what = Command.ITEM_GET_IMAGE; - command.arg1 = item.getId(); - dao.executeCommand(command); - - } - // process <-- - - - @Override - public void setPresenter(Presenter.ItemDetail presenter) { - this.presenter = presenter; - this.item = presenter.getCurrentItem(); - } - - @Override - public Presenter.ItemDetail getPresenter() { - return presenter; - } - - @Override - public void onEvent(Message event) { - - switch (event.what) { - case Event.ITEM_IMAGE_LOAD_ERROR : - break; - case Event.ITEM_IMAGE_LOAD_COMPLETED : - pic = (Bitmap) event.obj; - notifyDataSetChanged(); - break; - } - - } -} diff --git a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java b/app/src/main/java/hikapro/com/backpack/model/ItemModel.java deleted file mode 100644 index 3d94c9e..0000000 --- a/app/src/main/java/hikapro/com/backpack/model/ItemModel.java +++ /dev/null @@ -1,345 +0,0 @@ -package hikapro.com.backpack.model; - - -import android.os.Message; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Hashtable; -import java.util.List; - -import hikapro.com.backpack.model.dao.Command; -import hikapro.com.backpack.model.dao.DAO; -import hikapro.com.backpack.model.dao.Event; -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.presenter.Presenter; - -/** - * Created by tariel on 22/04/16. - */ -public class ItemModel implements Model.Item { - - protected Presenter.ItemList presenter; - private List rawCategories; - private List sortedCategories; - private List rawItems; - - protected DAO dao; - protected int currentSet; - protected int currentSetActiveItemsQty; - protected int packedQty; - protected Hashtable categoriesCache; - private List itemsCache; - private List itemsDiscardCache; - - private Hashtable> items; - protected Hashtable> cache; - - public ItemModel() { - this.rawCategories = new ArrayList<>(); - this.rawItems = new ArrayList<>(); - this.sortedCategories = new ArrayList<>(); - - this.categoriesCache = new Hashtable<>(20, 0.9f); - - this.packedQty = -1; - - this.itemsCache = new ArrayList<>(); - this.itemsDiscardCache = new ArrayList<>(); - this.cache = new Hashtable<>(12, 0.9f); - this.dao = DAO.getInstance(); - dao.registerObserver(this); - } - - // categories --> - - @Override - public Category getCategoryByPosition(int position) { - Category ret = null; - if (cache.containsKey(currentSet)) - ret = categoriesCache.get(cache.get(currentSet).get(position).getCategory()); - return ret; - - } - // categories <-- - - // items --> - - @Override - public int insertItem(Item item) { - List iList = cache.get(currentSet); - iList.add(item); - Collections.sort(iList); - cache.put(currentSet, iList); - ++currentSetActiveItemsQty; - // TODO write to database - return 0; - } - @Override - public boolean deleteItem(Item item) { - if (isPendingRemoval(item)) - itemsDiscardCache.remove(item); - cache.get(currentSet).remove(item); - --currentSetActiveItemsQty; - //TODO write to ds - - return false; - } - - @Override - public Item findItem(int id) { - List items = cache.get(currentSet); - Item item = null; - if (items != null) - { - for (Item i : items) { - if (i.getId() == id) { - item = i; - break; - } - } - } - return item; - } - @Override - public Item getItemByPosition(int position) { - Item ret = null; - if (cache.containsKey(currentSet)) - ret = cache.get(currentSet).get(position); - return ret; - } - - - @Override - public void filter(String query) { - - if (query.isEmpty()) { - Message command = Message.obtain(); - command.what = Command.SET_GET_ITEMS; - command.arg1 = presenter.getCurrentSet().getId(); - dao.executeCommand(command); - } else { - query = query.toLowerCase(); - String name; - List newList = new ArrayList<>(20); - List oldList = cache.get(currentSet); - for (Item item : oldList) { - name = item.getName().toLowerCase(); - if (name.contains(query)) { - newList.add(item); - } - } - cache.put(currentSet, newList); - } - } - - @Override - public int getHeaderId(int position) { - return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getCategory() : -1; - } - - @Override - public int getItemId(int position) { - return cache.containsKey(currentSet) ? cache.get(currentSet).get(position).getId() : -1; - } - - @Override - public void clear() { - if (cache.containsKey(currentSet)) - cache.get(currentSet).clear(); - } - - @Override - public boolean isPendingRemoval(Item item) { - return itemsDiscardCache.contains(item); - } - - @Override - public void pendingRemoveCancel(Item item) { - if (itemsDiscardCache.contains(item)) - itemsDiscardCache.remove(item); - } - - @Override - public void pendingRemove(Item item) { - itemsDiscardCache.add(item); - } - - @Override - public int getItemsCount() { - boolean is = cache.containsKey(currentSet); - return is ? cache.get(currentSet).size() : 0; - } - - @Override - public int getActiveItemsCount() { - return currentSetActiveItemsQty; - } - - @Override - public int getPackedQty() { - return packedQty; - } - // items <-- - - // events --> - - @Override - public void notifyDataSetChanged() { - presenter.notifyDataSetChanged(); - } - - @Override - public void onDestroy(boolean isConfigurationChanging) { - if ( !isConfigurationChanging ) { - presenter = null; - } - } - - private void sendMessage(String message) { - presenter.showMessage(message); - } - - // events <-- - - // process --> - - - @Override - public void packItem(int itemId) { - Item item = findItem(itemId); - if (item != null) - cache.get(currentSet).remove(item); - Message command = Message.obtain(); - command.what = Command.ITEM_PACK; - command.arg1 = currentSet; - command.arg2 = itemId; - dao.executeCommand(command); - } - - @Override - public void unpackItem(int itemId) { - // nothing - } - - @Override - public void unpackSet(int setId) { - // nothing - } - - @Override - public void executeQuery() { - Message command; - - if (cache.contains(currentSet)) { - notifyDataSetChanged(); - } else { - if (categoriesCache.isEmpty()) { - command = Message.obtain(); - command.what = Command.ITEM_GET_CATEGORIES; - dao.executeCommand(command); - } - command = Message.obtain(); - command.what = Command.SET_GET_ITEMS; - command.arg1 = presenter.getCurrentSet().getId(); - dao.executeCommand(command); - } - } - @Override - public void onEvent(Message event) { - - switch (event.what) { - case Event.SET_ITEMS_LOAD_ERROR : // TODO check - if (presenter != null) { - if (packedQty == -1) - packedQty = presenter.getCurrentSet().getPackedQty(); - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.SET_UNPACK_ERROR : - break; - case Event.ITEM_FROM_SET_ERROR : - break; - case Event.ITEM_DELETE_ERROR : - break; - case Event.ITEM_PACK_ERROR : - break; - case Event.ITEM_UNPACK_ERROR : - break; - case Event.ITEM_INSERT_ERROR : - break; - case Event.ITEM_CATEGORY_LOAD_ERROR : - break; - case Event.SET_ITEMS_LOAD_COMPLETED : - List res = (List) event.obj; - cache.put(event.arg1, res); - notifyDataSetChanged(); - if (presenter != null) { - if (packedQty == -1) - packedQty = presenter.getCurrentSet().getPackedQty(); - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.SET_UNPACK_COMPLETED : - packedQty = 0; - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.ITEM_CATEGORY_LOAD_COMPLETED : - categoriesCache = (Hashtable)event.obj; - break; - case Event.ITEM_FROM_SET_DELETED : - break; - case Event.ITEM_DELETED : - break; - case Event.ITEM_PACKED : - //TODO something - ++packedQty; - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.ITEM_UNPACKED : - --packedQty; - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.ITEM_INSERTED : - break; - } - } - - // process <-- - - // other --> - - - @Override - public void setPresenter(Presenter.ItemList presenter) { - this.presenter = presenter; - this.currentSet = presenter.getCurrentSet().getId(); - this.currentSetActiveItemsQty = presenter.getCurrentSet().getActiveQty(); - } - - @Override - public Presenter.ItemList getPresenter() { - return presenter; - } - - private Category findCategory(int categoryId) { - Category category = null; - - for (Category c : rawCategories) { - if (c.getId() == categoryId) { - category = c; - break; - } - } - return category; - } - // other <-- -} diff --git a/app/src/main/java/hikapro/com/backpack/model/Model.java b/app/src/main/java/hikapro/com/backpack/model/Model.java deleted file mode 100644 index 0cca863..0000000 --- a/app/src/main/java/hikapro/com/backpack/model/Model.java +++ /dev/null @@ -1,72 +0,0 @@ -package hikapro.com.backpack.model; - -import android.graphics.Bitmap; -import android.os.Message; - -import java.util.List; - -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.presenter.Presenter; - -/** - * Created by tariel on 19/04/16. - */ -public interface Model { - - interface Base { - void onDestroy(boolean isConfigurationChanging); - void executeQuery(); - void notifyDataSetChanged(); - void onEvent(Message event); - } - - interface Set extends Base { - hikapro.com.backpack.model.entities.Set getSetByPosition(int position); - hikapro.com.backpack.model.entities.Set findSet(int id); - int getSetsCount(); - void setPresenter(Presenter.SetList presenter); - Presenter.SetList getPresenter(); - //GLM - List getSets(); // tag renamed - void setsReorderNotify(); - } - - interface Item extends Base { - int insertItem(hikapro.com.backpack.model.entities.Item item); - boolean deleteItem(hikapro.com.backpack.model.entities.Item item); - void filter(String query); - int getHeaderId(int position);//TODO review - int getItemId(int position);//TODO review - void clear(); - boolean isPendingRemoval(hikapro.com.backpack.model.entities.Item item); - void pendingRemove(hikapro.com.backpack.model.entities.Item item); - void pendingRemoveCancel(hikapro.com.backpack.model.entities.Item item); - int getItemsCount(); - int getActiveItemsCount(); - int getPackedQty(); - - hikapro.com.backpack.model.entities.Item findItem(int id); - hikapro.com.backpack.model.entities.Item getItemByPosition(int position); - - hikapro.com.backpack.model.entities.Category getCategoryByPosition(int position); - void setPresenter(Presenter.ItemList presenter); - Presenter.ItemList getPresenter(); - - void packItem(int itemId); - void unpackItem(int itemId); - void unpackSet(int setId); - } - - interface Detail extends Base { - int getCount(); - hikapro.com.backpack.model.entities.Item findItem(int id); - Bitmap getPicture(); - void setPresenter(Presenter.ItemDetail presenter); - Presenter.ItemDetail getPresenter(); - } - - - -} diff --git a/app/src/main/java/hikapro/com/backpack/model/PackedModel.java b/app/src/main/java/hikapro/com/backpack/model/PackedModel.java deleted file mode 100644 index 6d18301..0000000 --- a/app/src/main/java/hikapro/com/backpack/model/PackedModel.java +++ /dev/null @@ -1,121 +0,0 @@ -package hikapro.com.backpack.model; - -import android.os.Message; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Hashtable; -import java.util.List; - -import hikapro.com.backpack.model.dao.Command; -import hikapro.com.backpack.model.dao.DAO; -import hikapro.com.backpack.model.dao.Event; -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.presenter.Presenter; - -/** - * Created by tariel on 12/05/16. - */ -public class PackedModel extends ItemModel { - - public PackedModel() { - super(); - } - - @Override - public void executeQuery() { - Message command; - - if (cache.contains(currentSet)) { - notifyDataSetChanged(); - } else { - if (categoriesCache.isEmpty()) { - command = Message.obtain(); - command.what = Command.ITEM_GET_CATEGORIES; - dao.executeCommand(command); - } - command = Message.obtain(); - command.what = Command.SET_GET_PACKED_ITEMS; - command.arg1 = presenter.getCurrentSet().getId(); - dao.executeCommand(command); - } - } - - @Override - public void onEvent(Message event) { - - switch (event.what) { - - case Event.SET_PACKED_LOAD_ERROR : - break; - case Event.SET_UNPACK_ERROR : - break; - case Event.ITEM_CATEGORY_LOAD_ERROR : - break; - case Event.ITEM_PACK_ERROR : - break; - case Event.ITEM_UNPACK_ERROR : - break; - case Event.SET_PACKED_LOAD_COMPLETED : - List res = (List) event.obj; - cache.put(event.arg1, res); - notifyDataSetChanged(); - packedQty = res.size(); - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.SET_UNPACK_COMPLETED : - packedQty = 0; - cache.get(event.arg2).clear(); - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.ITEM_CATEGORY_LOAD_COMPLETED : - categoriesCache = (Hashtable)event.obj; - break; - case Event.ITEM_PACKED : - //TODO something - ++packedQty; - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - case Event.ITEM_UNPACKED : - --packedQty; - if (presenter != null) { - presenter.notifyItemPackStatusChanged(); - } - break; - } - } - - @Override - public void unpackItem(int itemId) { - Message command; - command = Message.obtain(); - command.what = Command.ITEM_UNPACK; - command.arg1 = currentSet; - command.arg2 = itemId; - dao.executeCommand(command); - Item item = findItem(itemId); - if (item != null) - cache.get(currentSet).remove(item); - } - - @Override - public void packItem(int itemId) { - // nothing - } - - @Override - public void unpackSet(int setId) { - Message command; - command = Message.obtain(); - command.what = Command.SET_UNPACK_ITEMS; - command.arg1 = setId; - dao.executeCommand(command); - } -} diff --git a/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java b/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java deleted file mode 100644 index 6c7c003..0000000 --- a/app/src/main/java/hikapro/com/backpack/model/dao/DAO.java +++ /dev/null @@ -1,966 +0,0 @@ -package hikapro.com.backpack.model.dao; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Process; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import hikapro.com.backpack.App; -import hikapro.com.backpack.model.Api; -import hikapro.com.backpack.model.Model; -import hikapro.com.backpack.model.RestClient; -import hikapro.com.backpack.model.SetModel; -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.model.entities.Set; -import hikapro.com.backpack.model.entities.Timestamp; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -/** - * Created by tariel on 20/04/16. - */ -public class DAO { - //region Constants - private static final int CORE_POOL_SIZE = 1; - private static final int CORE_MAX_POOL_SIZE = 1; - - private static final int KEEP_ALIVE_TIME = 1; - private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; - - private static final int MY_LIST_ID = 15; - //endregion - - private static DAO instance; - - private final ThreadPoolExecutor threadPool; - private final Handler handler; - private SetModel setModel; - private final Api api; - private Context context; - private DbHelper helper; - private Map observers; - - private DAO() { - this.context = App.getAppContext(); - this.helper = new DbHelper(this.context); - this.api = RestClient.getApi(); - final BlockingQueue taskQueue = new LinkedBlockingQueue<>(); - this.threadPool = initPool(taskQueue); - this.observers = Collections.synchronizedMap(new HashMap()); - this.handler = initHandler(); - } - - static { - instance = new DAO(); - } - - public static DAO getInstance() { - return instance; - } - - public void registerObserver(Model.Base o) { - observers.put(o.getClass().getName(), o); - } - - private ThreadPoolExecutor initPool (BlockingQueue taskQueue) { - ThreadPoolExecutor ret = new ThreadPoolExecutor( - CORE_POOL_SIZE - ,CORE_MAX_POOL_SIZE - ,KEEP_ALIVE_TIME - ,KEEP_ALIVE_TIME_UNIT - ,taskQueue - ); - return ret; - } - - private Handler initHandler() { - Handler ret = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - for (Map.Entry entry : observers.entrySet()) { - entry.getValue().onEvent(msg); - } - } - }; - return ret; - } - - public void executeCommand(Message command) { - - SetTask setTask; - ItemTask itemTask; - ImageProviderTask imageProviderTask; - - if (command != null) { - switch (command.what) { - - case Command.SET_GET_ALL : - setTask = new SetTask(Command.SET_GET_ALL, - Process.THREAD_PRIORITY_MORE_FAVORABLE); - threadPool.execute(setTask); - break; - - case Command.SET_REORDER : - setTask = new SetTask(Command.SET_REORDER, - Process.THREAD_PRIORITY_BACKGROUND); - setTask.setsToUpdate = (List) command.obj; - threadPool.execute(setTask); - break; - - case Command.SET_GET_ITEMS : - setTask = new SetTask(Command.SET_GET_ITEMS, - Process.THREAD_PRIORITY_MORE_FAVORABLE); - setTask.setId = command.arg1; - threadPool.execute(setTask); - break; - - case Command.SET_GET_PACKED_ITEMS : - setTask = new SetTask(Command.SET_GET_PACKED_ITEMS, - Process.THREAD_PRIORITY_MORE_FAVORABLE); - setTask.setId = command.arg1; - threadPool.execute(setTask); - break; - - case Command.SET_UNPACK_ITEMS : - setTask = new SetTask(Command.SET_UNPACK_ITEMS, - Process.THREAD_PRIORITY_DEFAULT); - setTask.setId = command.arg1; - threadPool.execute(setTask); - - case Command.ITEM_GET_CATEGORIES : - itemTask = new ItemTask(Command.ITEM_GET_CATEGORIES, - Process.THREAD_PRIORITY_MORE_FAVORABLE); - threadPool.execute(itemTask); - break; - - case Command.ITEM_INSERT : - itemTask = new ItemTask(Command.ITEM_INSERT, - Process.THREAD_PRIORITY_BACKGROUND); - itemTask.item = (Item) command.obj; - itemTask.setId = command.arg1; - threadPool.execute(itemTask); - break; - - case Command.ITEM_DELETE_FROM_SET : - itemTask = new ItemTask(Command.ITEM_DELETE_FROM_SET, - Process.THREAD_PRIORITY_BACKGROUND); - itemTask.setId = command.arg1; - itemTask.itemId = command.arg2; - threadPool.execute(itemTask); - break; - - case Command.ITEM_PACK : - itemTask = new ItemTask(Command.ITEM_PACK, - Process.THREAD_PRIORITY_BACKGROUND); - itemTask.setId = command.arg1; - itemTask.itemId = command.arg2; - threadPool.execute(itemTask); - break; - - case Command.ITEM_UNPACK : - itemTask = new ItemTask(Command.ITEM_UNPACK, - Process.THREAD_PRIORITY_BACKGROUND); - itemTask.setId = command.arg1; - itemTask.itemId = command.arg2; - threadPool.execute(itemTask); - break; - - case Command.ITEM_GET_IMAGE : - Item item = findItem(command.arg1); - if (item != null) { - imageProviderTask = new ImageProviderTask(Command.ITEM_GET_IMAGE, - Process.THREAD_PRIORITY_DEFAULT); - imageProviderTask.item = item; - threadPool.execute(imageProviderTask); - } - break; - - case Command.MY_LIST_ITEM_ADD : - break; - - case Command.MY_LIST_ITEM_DELETE : - break; - - case Command.MY_LIST_POST : - break; - - case Command.MY_LIST_CLEAR : - break; - - case Command.SYNC : - threadPool.execute(new SyncTask(Command.SYNC, - Process.THREAD_PRIORITY_BACKGROUND)); - break; - - case Command.SYNC_IF_NOT_EXISTS : - threadPool.execute(new SyncTask(Command.SYNC_IF_NOT_EXISTS, - Process.THREAD_PRIORITY_MORE_FAVORABLE)); - break; - } - } - } - - /////////////////////// DATABASE ///////////////////// - - //region Database - private SQLiteDatabase getReadDB(){ - return helper.getReadableDatabase(); - } - - private SQLiteDatabase getWriteDB(){ - return helper.getWritableDatabase(); - } - - // inserts - private void insertTimestamp(Timestamp timestamp) { - if (timestamp != null && timestamp.timestamp > 0) { - ContentValues values; - SQLiteDatabase db = getWriteDB(); - try { - db.beginTransaction(); - values = Db.LogTable.toContentValues(timestamp); - db.insert(Db.LogTable.TABLE_NAME, null, values); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - db.close(); - } - } - } - private void insertItems(List items) { - if (items != null && !items.isEmpty()) { - ContentValues values; - SQLiteDatabase db = getWriteDB(); - try { - db.beginTransaction(); - for (Item item : items) { - values = Db.ItemsTable.toContentValues(item); - db.insert(Db.ItemsTable.TABLE_NAME, null, values); - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - db.close(); - } - } - } - private void insertCategories(List categories) { - - if (categories != null && !categories.isEmpty()) { - ContentValues values; - SQLiteDatabase db = getWriteDB(); - try { - db.beginTransaction(); - for (Category category : categories) { - values = Db.CategoriesTable.toContentValues(category); - db.insert(Db.CategoriesTable.TABLE_NAME, null, values); - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - db.close(); - } - } - } - private void insertSets(List sets) { - - if (sets != null && !sets.isEmpty()) { - ContentValues values; - int i = 0; - SQLiteDatabase db = getWriteDB(); - try { - db.beginTransaction(); - for (Set set : sets) { - values = Db.SetsTable.toContentValues(set, i); - db.insert(Db.SetsTable.TABLE_NAME, null, values); - insertSetItems(set, db); - ++i; - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - db.close(); - } - } - } - private void insertSetItems(Set set, SQLiteDatabase db) { - if (set != null && db != null) { - if (!set.getItems().isEmpty()) { - ContentValues values; - int setId = set.getId(); - try { - db.beginTransaction(); - for (Integer itemid : set.getItems()) { - values = Db.SetItemsTable.toContentValues(setId, itemid); - db.insert(Db.SetItemsTable.TABLE_NAME, null, values); - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - } - } - private void insertSetItem(int setId, int itemId) { - ContentValues values; - SQLiteDatabase db = getWriteDB(); - try { - db.beginTransaction(); - values = Db.SetItemsTable.toContentValues(setId, itemId); - db.insert(Db.SetItemsTable.TABLE_NAME, null, values); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - db.close(); - } - } - // reads - private boolean LogExist() { - boolean ret; - SQLiteDatabase db = getReadDB(); - Cursor cursor = db.query(Db.LogTable.TABLE_NAME, - new String[] {Db.LogTable.COLUMN_ID}, null, null, null, null, null, "1"); - ret = cursor.moveToNext(); - cursor.close(); - db.close(); - return ret; - } - - private Item findItem(int id) { - Item ret = null; - Cursor cursor = null; - SQLiteDatabase db = null; - try { - db = getReadDB(); - String query = String.format("SELECT * FROM %s a WHERE a.%s = %d LIMIT 1", - Db.ItemsTable.TABLE_NAME, Db.ItemsTable.COLUMN_ID, id); - cursor = db.rawQuery(query, null); - if (cursor.moveToNext()) { - ret = Db.ItemsTable.parseCursor(cursor); - } - } catch (Exception e) { - //TODO write to log here - } finally { - if (cursor != null) - cursor.close(); - if (db != null) - db.close(); - } - return ret; - } - - private List readItems(int setId, boolean packed) { - List ret = new ArrayList<>(256); - Cursor cursor = null; - SQLiteDatabase db = null; - Item item; - String query = String.format( - "SELECT a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s, a.%s FROM %s a INNER JOIN %s b ON a.%s = b.%s WHERE b.%s = ? AND b.%s <> 1 AND b.%s %s 1", - - Db.ItemsTable.COLUMN_ID, - Db.ItemsTable.COLUMN_NAME, - Db.ItemsTable.COLUMN_CATEGORY, - Db.ItemsTable.COLUMN_DESCRIPTION, - Db.ItemsTable.COLUMN_BUY_URLS, - Db.ItemsTable.COLUMN_PHOTO_URL, - Db.ItemsTable.COLUMN_PHOTO_THUMB_URL, - Db.ItemsTable.COLUMN_PHOTO_LOCAL, - Db.ItemsTable.COLUMN_PHOTO_THUMB_LOCAL, - - Db.ItemsTable.TABLE_NAME, - Db.SetItemsTable.TABLE_NAME, - Db.ItemsTable.COLUMN_ID, - Db.SetItemsTable.COLUMN_ITEM, - Db.SetItemsTable.COLUMN_SET, - Db.SetItemsTable.COLUMN_DELETED, - Db.SetItemsTable.COLUMN_PACKED, - packed ? "=" : "<>"); - try { - db = getReadDB(); - cursor = db.rawQuery(query, new String[]{String.valueOf(setId)}); - while (cursor.moveToNext()) { - item = Db.ItemsTable.parseCursor(cursor); - ret.add(item); - } - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (cursor != null) - cursor.close(); - if (db != null) - db.close(); - } - return ret; - } - private Hashtable readCategories() { - Hashtable ret = new Hashtable<>(20, 0.9f); - Cursor cursor = null; - SQLiteDatabase db = null; - Category category; - try { - db = getReadDB(); - cursor = db.query(Db.CategoriesTable.TABLE_NAME, - new String[]{Db.CategoriesTable.COLUMN_ID, - Db.CategoriesTable.COLUMN_NAME}, - null,null,null,null,null); - while (cursor.moveToNext()) { - category = Db.CategoriesTable.parseCursor(cursor); - ret.put(category.getId(), category); - } - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (cursor != null) - cursor.close(); - if (db != null) - db.close(); - } - return ret; - } - - private Set readSet(SQLiteDatabase db, int setId) { - Set ret = null; - Cursor cursor; - String query = String.format("SELECT * FROM %s a WHERE a.%s = %d LIMIT 1", - Db.SetsTable.TABLE_NAME, Db.SetsTable.COLUMN_ID, setId); - cursor = db.rawQuery(query, null); - if (cursor.moveToNext()) - ret = Db.SetsTable.parseCursor(cursor); - if (cursor != null) - cursor.close(); - return ret; - } - - private List readSets() { - List ret = new ArrayList<>(12); - Cursor cursor = null; - SQLiteDatabase db = null; - Set set; - try { - db = getReadDB(); - String query = String.format("SELECT * FROM %s", Db.SetsTable.TABLE_NAME); - cursor = db.rawQuery(query, null); - while (cursor.moveToNext()) { - set = Db.SetsTable.parseCursor(cursor); - ret.add(set); - } - Collections.sort(ret); - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (cursor != null) - cursor.close(); - if (db != null) - db.close(); - } - return ret; - } - // updates - private int updateItemLocalPic(int id, String path) { - int ret = 0; - SQLiteDatabase db = null; - ContentValues values; - try { - db = getWriteDB(); - db.beginTransaction(); - values = new ContentValues(); - values.put(Db.ItemsTable.COLUMN_PHOTO_LOCAL, path); - ret = db.update(Db.ItemsTable.TABLE_NAME, values, "_id = ?", - new String[]{String.valueOf(id)}); - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (db != null) { - db.endTransaction(); - db.close(); - } - } - return ret; - } - - private int updateSetsOrder(List reorderedSet) { - int ret = 0; - SQLiteDatabase db = null; - ContentValues values; - try { - - db = getWriteDB(); - db.beginTransaction(); - for (Set set : reorderedSet) { - values = new ContentValues(); - values.put(Db.SetsTable.COLUMN_LINE_NUMBER, set.getLineNumber()); - ret += db.update(Db.SetsTable.TABLE_NAME, values, "_id = ?", - new String[]{String.valueOf(set.getId())}); - } - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (db != null) { - db.endTransaction(); - db.close(); - } - } - return ret; - } - private int updateSetItemDeleted(int setId, int itemId, boolean del) { - int ret = 0; - SQLiteDatabase db = null; - ContentValues values; - try { - db = getWriteDB(); - db.beginTransaction(); - values = new ContentValues(); - values.put(Db.SetItemsTable.COLUMN_DELETED, del); - ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", - Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), - new String[]{String.valueOf(setId), String.valueOf(itemId)}); - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (db != null) { - db.endTransaction(); - db.close(); - } - } - return ret; - } - - private int updateSetItemsPacked(int setId, boolean pack) { - int ret = 0; - SQLiteDatabase db = null; - ContentValues values; - Set set; - try { - db = getWriteDB(); - db.beginTransaction(); - - /* - String query = String.format("UPDATE %s SET %s = %d where %s = %d", - Db.SetItemsTable.TABLE_NAME, - Db.SetItemsTable.COLUMN_PACKED, - pack, - Db.SetItemsTable.COLUMN_SET, - setId); - Cursor cursor = db.rawQuery(query, null);*/ - - values = new ContentValues(); - values.put(Db.SetItemsTable.COLUMN_PACKED, pack); - ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ?", - Db.SetItemsTable.COLUMN_SET), - new String[]{String.valueOf(setId)}); - - set = readSet(db, setId); - if (set != null) { - values = new ContentValues(); - values.put(Db.SetsTable.COLUMN_PACKED_QTY, pack ? set.getActiveQty() : 0); - ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", - Db.SetsTable.COLUMN_ID), - new String[]{String.valueOf(setId)}); - } - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (db != null) { - db.endTransaction(); - db.close(); - } - } - return ret; - } - - private int updateSetItemPacked(int setId, int itemId, boolean pack) { - int ret = 0; - SQLiteDatabase db = null; - ContentValues values; - Set set; - try { - db = getWriteDB(); - db.beginTransaction(); - - values = new ContentValues(); - values.put(Db.SetItemsTable.COLUMN_PACKED, pack); - ret = db.update(Db.SetItemsTable.TABLE_NAME, values, String.format("%s = ? AND %s = ?", - Db.SetItemsTable.COLUMN_SET, Db.SetItemsTable.COLUMN_ITEM ), - new String[]{String.valueOf(setId), String.valueOf(itemId)}); - - set = readSet(db, setId); - if (set != null) { - values = new ContentValues(); - values.put(Db.SetsTable.COLUMN_PACKED_QTY, pack ? set.getPackedQty() + 1 : set.getPackedQty() - 1); - ret += db.update(Db.SetsTable.TABLE_NAME, values, String.format("%s = ?", - Db.SetsTable.COLUMN_ID), - new String[]{String.valueOf(setId)}); - } - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - //TODO write to log here - - } catch (Exception e) { - //TODO write to log here - } finally { - if (db != null) { - db.endTransaction(); - db.close(); - } - } - return ret; - } - //endregion - - /////////////////////// TASK CLASSES ////////////////// - - //region Task classes - - // MY LIST CLASS - private class MyListTask implements Runnable { - int currentCommand; - int priority; - - - public MyListTask(int command, int priority) { - this.currentCommand = command; - this.priority = priority; - } - - @Override - public void run() { - android.os.Process.setThreadPriority(priority); - Message message = Message.obtain(); - switch (currentCommand) { - - } - handler.sendMessage(message); - } - } - // ITEM CLASS - private class ItemTask implements Runnable { - int currentCommand; - int priority; - int setId; - int itemId; - Item item; - - public ItemTask(int command, int priority) { - this.currentCommand = command; - this.setId = -1; - this.itemId = -1; - this.priority = priority; - } - - @Override - public void run() { - android.os.Process.setThreadPriority(priority); - Message message = Message.obtain(); - switch (currentCommand) { - - case Command.ITEM_DELETE_FROM_SET : - message.arg1 = updateSetItemDeleted(setId, itemId, true); - if (message.arg1 > 0) - message.what = Event.ITEM_FROM_SET_DELETED; - else - message.what = Event.ITEM_FROM_SET_ERROR; - break; - - case Command.ITEM_INSERT : - List items = new ArrayList<>(); - items.add(item); - if (items.isEmpty()) - message.what = Event.ITEM_INSERT_ERROR; - else { - insertItems(items); - insertSetItem(setId, item.getId()); - message.what = Event.ITEM_INSERTED; - message.arg1 = setId; - message.arg2 = item.getId(); - } - break; - - case Command.ITEM_PACK : - message.arg1 = updateSetItemPacked(setId, itemId, true); - if (message.arg1 > 0) - message.what = Event.ITEM_PACKED; - else - message.what = Event.ITEM_PACK_ERROR; - break; - - case Command.ITEM_UNPACK : - message.arg1 = updateSetItemPacked(setId, itemId, false); - if (message.arg1 > 0) - message.what = Event.ITEM_UNPACKED; - else - message.what = Event.ITEM_UNPACK_ERROR; - break; - - case Command.ITEM_GET_CATEGORIES : - Hashtable res = readCategories(); - if (res.isEmpty()) - message.what = Event.ITEM_CATEGORY_LOAD_ERROR; - else { - message.what = Event.ITEM_CATEGORY_LOAD_COMPLETED; - message.obj = res; - } - break; - } - handler.sendMessage(message); - } - } - // SET CLASS - private class SetTask implements Runnable { - int currentCommand; - int priority; - int setId; - List setsToUpdate; - List items; - - public SetTask(int command, int priority) { - this.currentCommand = command; - this.setId = -1; - this.setsToUpdate = new ArrayList<>(); - this.priority = priority; - } - - @Override - public void run() { - android.os.Process.setThreadPriority(priority); - Message message = Message.obtain(); - switch (currentCommand) { - - case Command.SET_GET_ALL : - List sets = readSets(); - if (sets.isEmpty()) - message.what = Event.SET_LOAD_ERROR; - else { - message.what = Event.SET_LOAD_COMPLETED; - message.obj = sets; - } - break; - - case Command.SET_GET_ITEMS : - items = readItems(setId, false); - if (items.isEmpty()) - message.what = Event.SET_ITEMS_LOAD_ERROR; - else { - Collections.sort(items); - message.what = Event.SET_ITEMS_LOAD_COMPLETED; - message.obj = items; - message.arg1 = setId; - } - break; - - case Command.SET_REORDER : - message.arg1 = updateSetsOrder(setsToUpdate); - if (message.arg1 > 0) - message.what = Event.SET_REORDER_COMPLETED; - else - message.what = Event.SET_REORDER_ERROR; - break; - - case Command.SET_GET_PACKED_ITEMS : - items = readItems(setId, true); - if (items.isEmpty()) - message.what = Event.SET_PACKED_LOAD_ERROR; - else { - Collections.sort(items); - message.what = Event.SET_PACKED_LOAD_COMPLETED; - message.obj = items; - message.arg1 = setId; - } - break; - - case Command.SET_UNPACK_ITEMS : - message.arg1 = updateSetItemsPacked(setId, false); - message.arg2 = setId; - if (message.arg1 > 0) - message.what = Event.SET_UNPACK_COMPLETED; - else - message.what = Event.SET_UNPACK_ERROR; - break; - } - handler.sendMessage(message); - } - } - // SYNC CLASS - private class SyncTask implements Runnable { - int currentCommand; - int priority; - int statusCode; - - public SyncTask(int command, int priority) { - this.currentCommand = command; - this.priority = priority; - } - - @Override - public void run() { - android.os.Process.setThreadPriority(priority); - Message message = Message.obtain(); - - switch (currentCommand) { - case Command.SYNC: - try { - Call> call = api.getSets(); - call.enqueue(new Callback>() { - @Override - public void onResponse(Call> call, Response> response) { - statusCode = response.code(); - // TODO - // check if first time - // if not check for updates else - // insert into database here - insertSets(response.body()); - } - @Override - public void onFailure(Call> call, Throwable t) { - } - }); - message.what = Event.SYNC_COMPLETED; - - } catch (Exception e) { - message.what = Event.SYNC_FAILED; - } - finally { - message.arg1 = statusCode; - handler.sendMessage(message); - } - break; - - case Command.SYNC_IF_NOT_EXISTS: - if (LogExist()) { - message.what = Event.SYNC_COMPLETED; - } else { - - try { - Response> response0 = api.getSets().execute(); - insertSets(response0.body()); - statusCode = response0.code(); - Response> response1 = api.getItemCategories().execute(); - insertCategories(response1.body()); - statusCode = response1.code(); - Response> response2 = api.getItems().execute(); - insertItems(response2.body()); - statusCode = response2.code(); - Response response3 = api.getTimestamp().execute(); - insertTimestamp(response3.body()); - statusCode = response3.code(); - message.what = Event.SYNC_COMPLETED; - } catch (IOException e ){ - message.what = Event.SYNC_FAILED; - } finally { - message.arg1 = statusCode; - handler.sendMessage(message); - } - } - break; - } - } - } - // IMAGE PROVIDER CLASS - private class ImageProviderTask implements Runnable { - int currentCommand; - int priority; - Item item; - - - public ImageProviderTask(int command, int priority) { - this.currentCommand = command; - this.priority = priority; - } - - @Override - public void run() { - android.os.Process.setThreadPriority(priority); - Message message = Message.obtain(); - switch (currentCommand) { - - case Command.ITEM_GET_IMAGE : - try { - Bitmap bitmap = loadItemImage(item); - if (bitmap != null) { - message.what = Event.ITEM_IMAGE_LOAD_COMPLETED; - message.obj = bitmap; - } - else - message.what = Event.ITEM_IMAGE_LOAD_ERROR; - } catch (Exception e) { - message.what = Event.ITEM_IMAGE_LOAD_ERROR; - } - break; - } - handler.sendMessage(message); - } - } - //endregion - - private Bitmap loadItemImage(Item item){ - ImageDownloadHelper downloadHelper = new ImageDownloadHelper(); - Bitmap bitmap = null; - String filename = null; - - if (item != null) { - bitmap = BitmapFactory.decodeFile(item.getPhotoLocal()); - // cannot retrieve, download and save then - if (bitmap == null) { // return it - bitmap = downloadHelper.loadImage(item.getPhotoUrl()); - if (bitmap != null) { - if (downloadHelper.isExternalStorageWritable()) { - filename = downloadHelper.saveImageExternal( - downloadHelper.generateFileName(item.getPhotoUrl()), bitmap); - } else { - filename = downloadHelper.saveImageInternal( - downloadHelper.generateFileName(item.getPhotoUrl()), bitmap); - } - updateItemLocalPic(item.getId(), filename); - } - } - } - return bitmap; - } - - -} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/ItemDetailPresenter.java b/app/src/main/java/hikapro/com/backpack/presenter/ItemDetailPresenter.java deleted file mode 100644 index 8fe9a0a..0000000 --- a/app/src/main/java/hikapro/com/backpack/presenter/ItemDetailPresenter.java +++ /dev/null @@ -1,129 +0,0 @@ -package hikapro.com.backpack.presenter; - -import android.content.Context; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.v7.widget.DefaultItemAnimator; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.widget.Toast; - -import java.lang.ref.WeakReference; - -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.Model; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.presenter.adapters.ItemDetailAdapter; -import hikapro.com.backpack.view.View; -import hikapro.com.backpack.view.recycler.DetailViewHolder; - -/** - * Created by tariel on 23/04/16. - */ -public class ItemDetailPresenter implements Presenter.ItemDetail { - - private WeakReference view; - private Model.Detail model; - private ItemDetailAdapter adapter; - private Item item; - - public ItemDetailPresenter() { - this.adapter = new ItemDetailAdapter(this); - } - - // life cycle --> - - @Override - public void onDestroy(boolean isChangingConfiguration) { - view = null; - model.onDestroy(isChangingConfiguration); - if ( !isChangingConfiguration ) { - model = null; - } - } - @Override - public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - android.view.View view = inflater.inflate(R.layout.fragment_item_detail, container, false); - LinearLayoutManager llm = new LinearLayoutManager(getActivityContext()); - RecyclerView detailRecycler = (RecyclerView) view.findViewById(R.id.item_detail_recycler); - detailRecycler.setLayoutManager(llm); - detailRecycler.setAdapter(adapter); - detailRecycler.setItemAnimator(new DefaultItemAnimator()); - model.executeQuery(); - return view; - } - @Override - public void onSaveInstanceState(Bundle outState) { - - } - // life cycle <-- - - // process --> - - @Override - public void notifyDataSetChanged() { - adapter.notifyDataSetChanged(); - } - - // process <-- - - // other impl --> - - @Override - public void setView(View.ItemDetail view) { - this.view = new WeakReference<>(view); - this.item = getView().getItem(); - } - @Override - public void setModel(Model.Detail model) { - this.model = model; - } - - @Override - public Model.Detail getModel() { - return model; - } - - @Override - public Item getCurrentItem() { - return item; - } - - @Override - public Context getAppContext() { - try { - return getView().getAppContext(); - } catch (NullPointerException e) { - return null; - } - } - @Override - public Context getActivityContext() { - try { - return getView().getActivityContext(); - } catch (NullPointerException e) { - return null; - } - } - @Override - public void showMessage(String message) { - Toast.makeText(getView().getAppContext(), message, Toast.LENGTH_SHORT).show(); - } - - @Override - public void displayPicture(Bitmap bitmap) { - - - } - // other impl <-- - - private View.ItemDetail getView() throws NullPointerException { - if ( view != null ) - return view.get(); - else - throw new NullPointerException("View is unavailable"); - } -} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemDetailAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemDetailAdapter.java deleted file mode 100644 index 2a44c6e..0000000 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemDetailAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -package hikapro.com.backpack.presenter.adapters; - -import android.graphics.Bitmap; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.Model; -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.view.recycler.DetailViewHolder; - -/** - * Created by tariel on 23/04/16. - */ -public class ItemDetailAdapter extends RecyclerView.Adapter { - - private Presenter.ItemDetail presenter; - - public ItemDetailAdapter(Presenter.ItemDetail presenter) { - this.presenter = presenter; - } - - @Override - public int getItemCount() { - return presenter.getModel().getCount(); - } - - @Override - public void onBindViewHolder(DetailViewHolder holder, int position) { - hikapro.com.backpack.model.entities.Item item = presenter.getModel().findItem(position); - holder.title.setText(item.getName()); - holder.description.setText(item.getDescription()); - Bitmap bitmap = presenter.getModel().getPicture(); - if (bitmap != null) - holder.photo.setImageBitmap(bitmap); - holder.title.setOnClickListener(new android.view.View.OnClickListener() { - @Override - public void onClick(android.view.View view) { - presenter.showMessage("On detail click"); - } - }); - } - - @Override - public DetailViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - DetailViewHolder viewHolder; - android.view.View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_detail, - parent, false); - viewHolder = new DetailViewHolder(v); - return viewHolder; - } - - -} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java deleted file mode 100644 index 0842b44..0000000 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/ItemListAdapter.java +++ /dev/null @@ -1,216 +0,0 @@ -package hikapro.com.backpack.presenter.adapters; - -import android.graphics.Color; -import android.os.Handler; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.HashMap; - -import hikapro.com.backpack.R; -import hikapro.com.backpack.model.entities.Category; -import hikapro.com.backpack.model.entities.Item; -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.adapters.helper.items.StickyHeaderAdapter; -import hikapro.com.backpack.view.recycler.HeaderViewHolder; -import hikapro.com.backpack.view.recycler.ItemViewHolder; - -/** - * Created by tariel on 01/05/16. - */ -public class ItemListAdapter extends RecyclerView.Adapter implements StickyHeaderAdapter { - - private static final int PENDING_REMOVAL_TIMEOUT = 4000; // 4sec - - boolean undoOn; // is undo on, you can turn it on from the toolbar menu - private Handler handler = new Handler(); // hanlder for running delayed runnables - HashMap pendingRunables = new HashMap<>(); // map of items to pending runnables, so we can cancel a removal if need be - private Presenter.ItemList presenter; - private boolean checkAll; - - public ItemListAdapter(Presenter.ItemList presenter) { - this.presenter = presenter; - } - - @Override - public int getItemCount() { - int res = presenter.getModel().getItemsCount(); - return res; - } - - @Override - public long getItemId(int position) { - long ret = presenter.getModel().getItemId(position); - return ret; - } - - @Override - public void onBindViewHolder(final ItemViewHolder holder, final int position) { - - final Item item = presenter.getModel().getItemByPosition(position); - if (presenter.getModel().isPendingRemoval(item)) { - // we need to show the "undo" state of the row - holder.itemView.setBackgroundColor(Color.RED); - holder.checkBox.setVisibility(View.GONE); - holder.undoButton.setVisibility(View.VISIBLE); - holder.undoButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // user wants to undo the removal, let's cancel the pending task - Runnable pendingRemovalRunnable = pendingRunables.get(item); - pendingRunables.remove(item); - if (pendingRemovalRunnable != null) handler.removeCallbacks(pendingRemovalRunnable); - presenter.getModel().pendingRemoveCancel(item); - // this will rebind the row in "normal" state - notifyItemChanged(position); - } - }); - } else { - holder.checkBox.setVisibility(View.VISIBLE); - holder.checkBox.setChecked(checkAll); - holder.checkBox.setText(item.getName() + " " + item.getId() + " pos " + position);//TODO del - holder.id = item.getId(); - holder.checkBox.setOnClickListener(new android.view.View.OnClickListener() { - @Override - public void onClick(View v) { - if (holder.checkBox.isChecked()) { - pack(holder.id, position); - // presenter.packItem(); TODO wtf - } else { - unpack(holder.id, position); - // presenter.unpackItem(); TODO wtf - } - } - }); - holder.infoButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - presenter.showDetails(holder.id); - } - }); - holder.itemView.setBackgroundColor(0x33FF99); - holder.undoButton.setVisibility(View.GONE); - holder.undoButton.setOnClickListener(null); - } - - } - - @Override - public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - ItemViewHolder viewHolder; - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, - parent, false); - viewHolder = new ItemViewHolder(v); - return viewHolder; - } - - public void setUndoOn(boolean undoOn) { - this.undoOn = undoOn; - } - - public boolean isUndoOn() { - return undoOn; - } - - public void add(Item item) { - presenter.getModel().insertItem(item); - notifyDataSetChanged(); - } - - - public void clear() { - presenter.getModel().clear(); - notifyDataSetChanged(); - } - - public void filter(String query) { - presenter.getModel().filter(query); - notifyDataSetChanged(); - } - - @Override - public long getHeaderId(int position) { - /*if (position == 0) { - return -1; - } else {*/ - return presenter.getModel().getHeaderId(position); - //} - } - - @Override - public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { - HeaderViewHolder viewHolder; - View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.view_header, parent, false); - viewHolder = new HeaderViewHolder(v); - return viewHolder; - } - - @Override - public void onBindHeaderViewHolder(HeaderViewHolder holder, int position) { - Category category = presenter.getModel().getCategoryByPosition(position); - holder.id = category.getId(); - holder.title.setText(category.getName()); - holder.title.setBackgroundColor(0x2B1E15); - } - - public void pendingRemoval(final int position) { - final Item item = presenter.getModel().getItemByPosition(position); - - if (! presenter.getModel().isPendingRemoval(item)) { - presenter.getModel().pendingRemove(item); - // this will redraw row in "undo" state - notifyItemChanged(position); - // let's create, store and post a runnable to remove the item - Runnable pendingRemovalRunnable = new Runnable() { - @Override - public void run() { - remove(item, position); - } - }; - handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT); - pendingRunables.put(item, pendingRemovalRunnable); - } - } - - public void remove(Item item, int position) { - presenter.getModel().deleteItem(item); - notifyItemRemoved(position); - } - - public void remove(int position) { - Item item = presenter.getModel().getItemByPosition(position); - presenter.getModel().deleteItem(item); - notifyItemRemoved(position); - } - - public boolean isPendingRemoval(int position) { - Item item = presenter.getModel().getItemByPosition(position); - return presenter.getModel().isPendingRemoval(item); - } - - public void pack(int itemId, int position) { - presenter.getModel().packItem(itemId); - notifyDataSetChanged(); - /* - notifyItemRemoved(position); - notifyItemRangeRemoved(position, getItemCount()); - */ - } - - public void unpack(int itemId, int position) { - presenter.getModel().unpackItem(itemId); - //notifyItemRemoved(position); - notifyDataSetChanged(); - } - - public void unpackAll() { - - } - - public void setCheckAll(boolean checkAll) { - this.checkAll = checkAll; - } -} diff --git a/app/src/main/java/hikapro/com/backpack/presenter/adapters/PackedListAdapter.java b/app/src/main/java/hikapro/com/backpack/presenter/adapters/PackedListAdapter.java deleted file mode 100644 index 2d862b0..0000000 --- a/app/src/main/java/hikapro/com/backpack/presenter/adapters/PackedListAdapter.java +++ /dev/null @@ -1,14 +0,0 @@ -package hikapro.com.backpack.presenter.adapters; - -import hikapro.com.backpack.presenter.Presenter; -import hikapro.com.backpack.presenter.adapters.ItemListAdapter; - -/** - * Created by tariel on 12/05/16. - */ -public class PackedListAdapter extends ItemListAdapter { - - public PackedListAdapter(Presenter.ItemList presenter) { - super(presenter); - } -} diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/DetailViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/DetailViewHolder.java deleted file mode 100644 index ed2e380..0000000 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/DetailViewHolder.java +++ /dev/null @@ -1,29 +0,0 @@ -package hikapro.com.backpack.view.recycler; - -import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import hikapro.com.backpack.R; - -/** - * Created by tariel on 23/04/16. - */ -public class DetailViewHolder extends RecyclerView.ViewHolder { - - public TextView title; - public TextView description; - public ImageView photo; - - public DetailViewHolder(View v) { - super(v); - setupViews(v); - } - - private void setupViews(View view) { - title = (TextView) view.findViewById(R.id.item_title); - description = (TextView) view.findViewById(R.id.item_description); - photo = (ImageView) view.findViewById(R.id.item_photo); - } -} diff --git a/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java b/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java deleted file mode 100644 index 618f83a..0000000 --- a/app/src/main/java/hikapro/com/backpack/view/recycler/ItemViewHolder.java +++ /dev/null @@ -1,34 +0,0 @@ -package hikapro.com.backpack.view.recycler; - -import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.ImageButton; -import android.widget.TextView; - -import hikapro.com.backpack.R; - - -/** - * Created by tariel on 01/05/16. - */ -public class ItemViewHolder extends RecyclerView.ViewHolder { - - public int id; - public CheckBox checkBox; - public Button undoButton; - public ImageButton infoButton; - - public ItemViewHolder(View v) { - super(v); - setupViews(v); - } - - private void setupViews(View view) { - infoButton = (ImageButton) view.findViewById(R.id.info_button); - checkBox = (CheckBox) view.findViewById(R.id.item_checkbox); - undoButton = (Button) view.findViewById(R.id.undo_button); - } - -} diff --git a/app/src/main/res/drawable-hdpi/ic_check_box_outline_blank_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_check_box_outline_blank_white_24dp.png new file mode 100644 index 0000000..443e73f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check_box_outline_blank_white_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_check_box_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_check_box_white_24dp.png new file mode 100644 index 0000000..9f3bc73 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check_box_white_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_info_white.png b/app/src/main/res/drawable-hdpi/ic_info_white.png new file mode 100644 index 0000000..2d316a3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_info_white.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check_box_outline_blank_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_check_box_outline_blank_white_24dp.png new file mode 100644 index 0000000..c3c14dd Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check_box_outline_blank_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check_box_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_check_box_white_24dp.png new file mode 100644 index 0000000..fa22907 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check_box_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_info_white.png b/app/src/main/res/drawable-mdpi/ic_info_white.png new file mode 100644 index 0000000..2d316a3 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_info_white.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check_box_outline_blank_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_check_box_outline_blank_white_24dp.png new file mode 100644 index 0000000..6c335dc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check_box_outline_blank_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check_box_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_check_box_white_24dp.png new file mode 100644 index 0000000..d159855 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check_box_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_info_white.png b/app/src/main/res/drawable-xhdpi/ic_info_white.png new file mode 100644 index 0000000..88acdc2 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_info_white.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_box_outline_blank_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_box_outline_blank_white_24dp.png new file mode 100644 index 0000000..339e57c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check_box_outline_blank_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_box_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_check_box_white_24dp.png new file mode 100644 index 0000000..3287ddf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check_box_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_info_white.png b/app/src/main/res/drawable-xxhdpi/ic_info_white.png new file mode 100644 index 0000000..9ed5c04 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_info_white.png differ diff --git a/app/src/main/res/drawable/cat_btn_bg.xml b/app/src/main/res/drawable/cat_btn_bg.xml new file mode 100644 index 0000000..33d2523 --- /dev/null +++ b/app/src/main/res/drawable/cat_btn_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/checkbox_selector.xml b/app/src/main/res/drawable/checkbox_selector.xml new file mode 100644 index 0000000..fe67d3f --- /dev/null +++ b/app/src/main/res/drawable/checkbox_selector.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/divider.xml b/app/src/main/res/drawable/divider.xml new file mode 100644 index 0000000..2f37952 --- /dev/null +++ b/app/src/main/res/drawable/divider.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/divider2.xml b/app/src/main/res/drawable/divider2.xml new file mode 100644 index 0000000..9a66035 --- /dev/null +++ b/app/src/main/res/drawable/divider2.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/facebook_48.png b/app/src/main/res/drawable/facebook_48.png new file mode 100644 index 0000000..eb0a202 Binary files /dev/null and b/app/src/main/res/drawable/facebook_48.png differ diff --git a/app/src/main/res/drawable/ic_check_box_outline_blank_white_24dp.png b/app/src/main/res/drawable/ic_check_box_outline_blank_white_24dp.png new file mode 100644 index 0000000..443e73f Binary files /dev/null and b/app/src/main/res/drawable/ic_check_box_outline_blank_white_24dp.png differ diff --git a/app/src/main/res/drawable/ic_check_box_white_24dp.png b/app/src/main/res/drawable/ic_check_box_white_24dp.png new file mode 100644 index 0000000..9f3bc73 Binary files /dev/null and b/app/src/main/res/drawable/ic_check_box_white_24dp.png differ diff --git a/app/src/main/res/drawable/ic_info_white.png b/app/src/main/res/drawable/ic_info_white.png new file mode 100644 index 0000000..2d316a3 Binary files /dev/null and b/app/src/main/res/drawable/ic_info_white.png differ diff --git a/app/src/main/res/drawable/list_background.png b/app/src/main/res/drawable/list_background.png new file mode 100644 index 0000000..e61c593 Binary files /dev/null and b/app/src/main/res/drawable/list_background.png differ diff --git a/app/src/main/res/drawable/nice_btn_bg.xml b/app/src/main/res/drawable/nice_btn_bg.xml new file mode 100644 index 0000000..000d9f3 --- /dev/null +++ b/app/src/main/res/drawable/nice_btn_bg.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/pic.png b/app/src/main/res/drawable/pic.png new file mode 100644 index 0000000..7ab8b3a Binary files /dev/null and b/app/src/main/res/drawable/pic.png differ diff --git a/app/src/main/res/drawable/search_divider.xml b/app/src/main/res/drawable/search_divider.xml new file mode 100644 index 0000000..d70309e --- /dev/null +++ b/app/src/main/res/drawable/search_divider.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/twitter_48.png b/app/src/main/res/drawable/twitter_48.png new file mode 100644 index 0000000..c3e985b Binary files /dev/null and b/app/src/main/res/drawable/twitter_48.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2a8e37d..de4e2f0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:orientation="horizontal" android:fitsSystemWindows="true" - tools:context="hikapro.com.backpack.MainActivity"> + tools:context="com.hikapro.backpack.MainActivity"> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/divider.xml b/app/src/main/res/layout/divider.xml new file mode 100644 index 0000000..dc17d06 --- /dev/null +++ b/app/src/main/res/layout/divider.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/footer.xml b/app/src/main/res/layout/footer.xml index d9885a0..7245009 100644 --- a/app/src/main/res/layout/footer.xml +++ b/app/src/main/res/layout/footer.xml @@ -2,25 +2,24 @@ + android:background="@color/colorFooterBackground" + android:layout_gravity="bottom"> - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add.xml b/app/src/main/res/layout/fragment_add.xml new file mode 100644 index 0000000..770709d --- /dev/null +++ b/app/src/main/res/layout/fragment_add.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_item_detail.xml b/app/src/main/res/layout/fragment_item_detail.xml index 0daaa99..c987dc9 100644 --- a/app/src/main/res/layout/fragment_item_detail.xml +++ b/app/src/main/res/layout/fragment_item_detail.xml @@ -1,17 +1,77 @@ - + android:animateLayoutChanges="true" + android:background="@color/colorUiMainbackground2"> - + + + + - + android:scrollbars="vertical" + android:scrollIndicators="none"/> + + + + - + + + + + + + + diff --git a/app/src/main/res/layout/fragment_item_list.xml b/app/src/main/res/layout/fragment_item_list.xml index a3ce8f2..32757ea 100644 --- a/app/src/main/res/layout/fragment_item_list.xml +++ b/app/src/main/res/layout/fragment_item_list.xml @@ -1,48 +1,49 @@ + android:orientation="vertical" + android:background="@color/colorListBackground"> - + android:scrollIndicators="none"/> + android:background="@color/colorFooterBackground"> diff --git a/app/src/main/res/layout/fragment_packed_list.xml b/app/src/main/res/layout/fragment_packed_list.xml index f238f47..75b770f 100644 --- a/app/src/main/res/layout/fragment_packed_list.xml +++ b/app/src/main/res/layout/fragment_packed_list.xml @@ -1,66 +1,67 @@ - + + android:background="@color/colorFooterBackground"> -