├── .gitignore ├── Azoft-Collage ├── .gitignore ├── build.gradle ├── libs │ ├── injectorLib.jar │ ├── licences.txt │ └── loaderLib.jar ├── libssrc │ ├── injectorLib-sources.jar │ └── loaderLib-sources.jar ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── azoft │ │ └── azoft │ │ └── collage │ │ ├── app │ │ └── CollageApplication.java │ │ ├── data │ │ ├── Collage.java │ │ ├── CollageFillData.java │ │ ├── CollageRegionData.java │ │ ├── Image.java │ │ ├── Post.java │ │ ├── User.java │ │ └── results │ │ │ ├── UserDataResult.java │ │ │ ├── UserRecentFeedResult.java │ │ │ └── UsersSearchResult.java │ │ ├── exceptions │ │ ├── CollageCreationException.java │ │ ├── DiskWriteException.java │ │ ├── InternalServerException.java │ │ ├── NoHandleInBaseActivityException.java │ │ └── NotAllowedException.java │ │ ├── loaders │ │ ├── CollageBigImageLoader.java │ │ ├── CollagePreviewCreatorLoader.java │ │ ├── UserDataLoader.java │ │ ├── UserImageFeedLoader.java │ │ └── UsersSearchLoader.java │ │ ├── server │ │ ├── InstagramService.java │ │ ├── RetrofitHelper.java │ │ ├── ServerConnector.java │ │ └── responces │ │ │ ├── SearchUserResponse.java │ │ │ └── UserRecentFeedResponse.java │ │ ├── ui │ │ ├── activities │ │ │ └── phone │ │ │ │ ├── CollageActivity.java │ │ │ │ ├── CollageAuthActivity.java │ │ │ │ ├── CollageBuilderActivity.java │ │ │ │ ├── CollagePreviewActivity.java │ │ │ │ ├── ImageSelectionFromUserFeedActivity.java │ │ │ │ ├── InstagramAuthActivity.java │ │ │ │ ├── SingleFragmentActivity.java │ │ │ │ ├── StartActivity.java │ │ │ │ └── UserSelectionActivity.java │ │ ├── adapters │ │ │ ├── CollageAdapter.java │ │ │ ├── UserFeedAdapter.java │ │ │ └── UsersAdapter.java │ │ ├── fragments │ │ │ ├── ActionBarLoaderFragment.java │ │ │ ├── CollageBuilderFragment.java │ │ │ ├── CollagePreviewFragment.java │ │ │ ├── CollageSelectionFragment.java │ │ │ ├── ImageSelectionFromUserFeedFragment.java │ │ │ └── UserSelectionFragment.java │ │ └── widgets │ │ │ ├── CollageItemView.java │ │ │ ├── CollageViewGroup.java │ │ │ └── SquareRelativeLayout.java │ │ └── utils │ │ ├── CollageRegion.java │ │ ├── CommonUtils.java │ │ ├── MediaUtils.java │ │ └── collagegenerators │ │ ├── CollageFactory.java │ │ └── SimpleCollageGenerator.java │ └── res │ ├── drawable-hdpi │ ├── ic_action_picture.png │ ├── ic_action_refresh.png │ ├── ic_action_share.png │ ├── ic_action_user.png │ ├── ic_launcher.png │ └── selected_collage.png │ ├── drawable-mdpi │ ├── ic_action_picture.png │ ├── ic_action_refresh.png │ ├── ic_action_share.png │ ├── ic_action_user.png │ ├── ic_launcher.png │ └── selected_collage.png │ ├── drawable-xhdpi │ ├── ic_action_picture.png │ ├── ic_action_refresh.png │ ├── ic_action_share.png │ ├── ic_action_user.png │ ├── ic_launcher.png │ └── selected_collage.png │ ├── drawable-xxhdpi │ ├── ic_action_picture.png │ ├── ic_action_refresh.png │ ├── ic_action_share.png │ └── ic_launcher.png │ ├── drawable │ └── ic_action_new.9.png │ ├── layout │ ├── activity_instagram_auth.xml │ ├── activity_single_fragment.xml │ ├── activity_start.xml │ ├── fragment_collage_builder.xml │ ├── fragment_collage_preview.xml │ ├── fragment_collage_selection.xml │ ├── fragment_image_selection_from_user_feed.xml │ ├── fragment_user_selection.xml │ ├── item_collage.xml │ ├── item_user.xml │ └── item_user_feed.xml │ ├── menu │ ├── collage_builder.xml │ ├── collage_preview.xml │ ├── image_selection_user_feed.xml │ ├── start_collage.xml │ └── user_account_selection.xml │ ├── values-ru │ └── strings.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ └── filepaths.xml ├── build.gradle ├── device-2016-01-15-125125.mp4 ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── privacy_policy.html ├── readme.md └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | gen 3 | workspace.xml 4 | build 5 | /.idea 6 | /*.iml 7 | /local.properties 8 | -------------------------------------------------------------------------------- /Azoft-Collage/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Azoft-Collage/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion '21.1.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 9 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | applicationId "com.azoft.azoft.collage" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 18 | } 19 | } 20 | lintOptions { 21 | abortOnError false 22 | } 23 | } 24 | 25 | dependencies { 26 | compile 'com.android.support:appcompat-v7:23.1.1' 27 | compile 'com.android.support:support-v4:23.1.1' 28 | compile 'com.squareup.okhttp:okhttp:2.0.0' 29 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0' 30 | compile 'com.squareup.retrofit:retrofit:1.9.0' 31 | compile 'com.squareup.picasso:picasso:2.4.0' 32 | compile fileTree(dir: 'libs', include: ['*.jar']) 33 | } -------------------------------------------------------------------------------- /Azoft-Collage/libs/injectorLib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/libs/injectorLib.jar -------------------------------------------------------------------------------- /Azoft-Collage/libs/licences.txt: -------------------------------------------------------------------------------- 1 | OkHttp (http://square.github.io/okhttp/) 2 | 3 | 4 | Copyright 2013 Square, Inc. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | 19 | 20 | 21 | Retrofit (http://square.github.io/retrofit/) 22 | 23 | 24 | Copyright 2013 Square, Inc. 25 | 26 | Licensed under the Apache License, Version 2.0 (the "License"); 27 | you may not use this file except in compliance with the License. 28 | You may obtain a copy of the License at 29 | 30 | http://www.apache.org/licenses/LICENSE-2.0 31 | 32 | Unless required by applicable law or agreed to in writing, software 33 | distributed under the License is distributed on an "AS IS" BASIS, 34 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | See the License for the specific language governing permissions and 36 | limitations under the License. 37 | 38 | 39 | 40 | 41 | Picasso (http://square.github.io/picasso/) 42 | 43 | 44 | Copyright 2013 Square, Inc. 45 | 46 | Licensed under the Apache License, Version 2.0 (the "License"); 47 | you may not use this file except in compliance with the License. 48 | You may obtain a copy of the License at 49 | 50 | http://www.apache.org/licenses/LICENSE-2.0 51 | 52 | Unless required by applicable law or agreed to in writing, software 53 | distributed under the License is distributed on an "AS IS" BASIS, 54 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 55 | See the License for the specific language governing permissions and 56 | limitations under the License. 57 | 58 | 59 | 60 | 61 | Gson (https://code.google.com/p/google-gson/) 62 | 63 | 64 | Licensed under the Apache License, Version 2.0 (the "License"); 65 | you may not use this file except in compliance with the License. 66 | You may obtain a copy of the License at 67 | 68 | http://www.apache.org/licenses/LICENSE-2.0 69 | 70 | Unless required by applicable law or agreed to in writing, software 71 | distributed under the License is distributed on an "AS IS" BASIS, 72 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 73 | See the License for the specific language governing permissions and 74 | limitations under the License. 75 | 76 | 77 | 78 | 79 | InjectorLib (https://github.com/mig35/LoaderLib) 80 | 81 | 82 | Licensed under the Apache License, Version 2.0 (the "License"); 83 | you may not use this file except in compliance with the License. 84 | You may obtain a copy of the License at 85 | 86 | http://www.apache.org/licenses/LICENSE-2.0 87 | 88 | Unless required by applicable law or agreed to in writing, software 89 | distributed under the License is distributed on an "AS IS" BASIS, 90 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 91 | See the License for the specific language governing permissions and 92 | limitations under the License. 93 | 94 | 95 | 96 | 97 | LoaderLib (https://github.com/mig35/LoaderLib) 98 | 99 | 100 | Licensed under the Apache License, Version 2.0 (the "License"); 101 | you may not use this file except in compliance with the License. 102 | You may obtain a copy of the License at 103 | 104 | http://www.apache.org/licenses/LICENSE-2.0 105 | 106 | Unless required by applicable law or agreed to in writing, software 107 | distributed under the License is distributed on an "AS IS" BASIS, 108 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 109 | See the License for the specific language governing permissions and 110 | limitations under the License. -------------------------------------------------------------------------------- /Azoft-Collage/libs/loaderLib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/libs/loaderLib.jar -------------------------------------------------------------------------------- /Azoft-Collage/libssrc/injectorLib-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/libssrc/injectorLib-sources.jar -------------------------------------------------------------------------------- /Azoft-Collage/libssrc/loaderLib-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/libssrc/loaderLib-sources.jar -------------------------------------------------------------------------------- /Azoft-Collage/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:/Users/Mikhail/Documents/tools/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /Azoft-Collage/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 36 | 37 | 40 | 41 | 44 | 45 | 50 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Azoft-Collage/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/app/CollageApplication.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.app; 2 | 3 | import android.app.Application; 4 | import android.os.Handler; 5 | import android.os.Looper; 6 | import android.widget.Toast; 7 | 8 | import com.squareup.picasso.Picasso; 9 | 10 | /** 11 | * Date: 4/7/2014 12 | * Time: 3:57 PM 13 | * 14 | * @author MiG35 15 | */ 16 | public class CollageApplication extends Application { 17 | 18 | private static CollageApplication sInstance; 19 | 20 | private Toast mOldToast; 21 | private Handler mHandler; 22 | 23 | @Override 24 | public void onCreate() { 25 | super.onCreate(); 26 | 27 | sInstance = this; 28 | mHandler = new Handler(); 29 | 30 | final Picasso picasso = Picasso.with(this); 31 | picasso.setIndicatorsEnabled(true); 32 | picasso.setLoggingEnabled(false); 33 | } 34 | 35 | public static CollageApplication getInstance() { 36 | return sInstance; 37 | } 38 | 39 | /** 40 | * Show toast. Previous toast will be canceled. 41 | * Thread save. 42 | * 43 | * @param message data to show for toast 44 | * @param gravity if set, this gravity will be used for toast 45 | */ 46 | public void showToast(final String message, final Integer gravity) { 47 | if (getMainLooper() == Looper.myLooper()) { 48 | if (null != mOldToast) { 49 | mOldToast.cancel(); 50 | } 51 | mOldToast = Toast.makeText(this, message, Toast.LENGTH_LONG); 52 | if (null != gravity) { 53 | mOldToast.setGravity(gravity, 0, 0); 54 | } 55 | mOldToast.show(); 56 | } else { 57 | mHandler.post(new Runnable() { 58 | @Override 59 | public void run() { 60 | showToast(message, gravity); 61 | } 62 | }); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/Collage.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data; 2 | 3 | import com.azoft.azoft.collage.utils.CollageRegion; 4 | 5 | import java.io.Serializable; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | /** 10 | * Contains collage structure as CollageRegion's list. 11 | *

12 | * Date: 4/8/2014 13 | * Time: 4:12 PM 14 | * 15 | * @author MiG35 16 | */ 17 | public class Collage implements Serializable { 18 | 19 | private static final long serialVersionUID = -3545166311422144328L; 20 | 21 | protected final List mCollageRegions; 22 | private final int mRegionsCount; 23 | 24 | /** 25 | * @param collageRegions - can't be null or empty 26 | */ 27 | public Collage(final List collageRegions) { 28 | if (null == collageRegions || collageRegions.isEmpty()) { 29 | throw new IllegalArgumentException("collageRegions shouldn't be empty"); 30 | } 31 | mCollageRegions = Collections.unmodifiableList(collageRegions); 32 | mRegionsCount = mCollageRegions.size(); 33 | } 34 | 35 | public List getCollageRegions() { 36 | return mCollageRegions; 37 | } 38 | 39 | public int getRegionsCount() { 40 | return mRegionsCount; 41 | } 42 | 43 | @Override 44 | public boolean equals(final Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (!(o instanceof Collage)) { 49 | return false; 50 | } 51 | 52 | final Collage collage = (Collage) o; 53 | 54 | return mCollageRegions.equals(collage.mCollageRegions); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return mCollageRegions.hashCode(); 60 | } 61 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/CollageFillData.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data; 2 | 3 | import com.azoft.azoft.collage.utils.CollageRegion; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * Contains collage fill data for all its regions. 10 | *

11 | * Date: 4/9/2014 12 | * Time: 11:40 AM 13 | * 14 | * @author MiG35 15 | */ 16 | public class CollageFillData extends Collage { 17 | 18 | private static final long serialVersionUID = 9161249919940778595L; 19 | 20 | private final Map mRectangleDataMap; 21 | 22 | public CollageFillData(final Collage collage) { 23 | super(collage.getCollageRegions()); 24 | 25 | mRectangleDataMap = new HashMap<>(); 26 | } 27 | 28 | public void setRegionData(final CollageRegion collageRegion, final CollageRegionData collageRegionData) { 29 | if (mCollageRegions.contains(collageRegion)) { 30 | mRectangleDataMap.put(collageRegion, collageRegionData); 31 | } else { 32 | throw new IllegalArgumentException("wrong collageRegion entered"); 33 | } 34 | } 35 | 36 | public CollageRegionData getRegionData(final CollageRegion collageRegion) { 37 | return mRectangleDataMap.get(collageRegion); 38 | } 39 | 40 | public boolean hasAllRegions() { 41 | if (mRectangleDataMap.size() == mCollageRegions.size()) { 42 | for (final Map.Entry entryItem : mRectangleDataMap.entrySet()) { 43 | if (null == entryItem.getValue()) { 44 | return false; 45 | } 46 | } 47 | return true; 48 | } 49 | return false; 50 | } 51 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/CollageRegionData.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data; 2 | 3 | import java.io.File; 4 | import java.io.Serializable; 5 | 6 | /** 7 | * Contains image data as source and it's position inside region. 8 | *

9 | * Date: 4/9/2014 10 | * Time: 11:42 AM 11 | * 12 | * @author MiG35 13 | */ 14 | public class CollageRegionData implements Serializable { 15 | 16 | private static final long serialVersionUID = -7032708611705926844L; 17 | 18 | private final File mImageFile; 19 | private Float mImageLeft; 20 | private Float mImageTop; 21 | private float mImageScale; 22 | private int mImageRealSizeScale; 23 | 24 | public CollageRegionData(final File imageFile) { 25 | if (null == imageFile) { 26 | throw new IllegalArgumentException("imageFile can't be null"); 27 | } 28 | mImageFile = imageFile; 29 | mImageScale = 1f; 30 | } 31 | 32 | public File getImageFile() { 33 | return mImageFile; 34 | } 35 | 36 | public Float getImageLeft() { 37 | return mImageLeft; 38 | } 39 | 40 | public void setImageLeft(final float imageLeft) { 41 | mImageLeft = imageLeft; 42 | } 43 | 44 | public Float getImageTop() { 45 | return mImageTop; 46 | } 47 | 48 | public void setImageTop(final float imageTop) { 49 | mImageTop = imageTop; 50 | } 51 | 52 | public float getImageScale() { 53 | return mImageScale; 54 | } 55 | 56 | public void setImageScale(final float imageScale) { 57 | mImageScale = imageScale; 58 | } 59 | 60 | @Override 61 | public boolean equals(final Object o) { 62 | if (this == o) { 63 | return true; 64 | } 65 | if (!(o instanceof CollageRegionData)) { 66 | return false; 67 | } 68 | 69 | final CollageRegionData collageRegionData = (CollageRegionData) o; 70 | 71 | return mImageFile.equals(collageRegionData.mImageFile); 72 | } 73 | 74 | @SuppressWarnings("NonFinalFieldReferencedInHashCode") 75 | @Override 76 | public int hashCode() { 77 | return mImageFile.hashCode(); 78 | } 79 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/Image.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Date: 4/9/2014 7 | * Time: 9:40 AM 8 | * 9 | * @author MiG35 10 | */ 11 | public class Image implements Serializable { 12 | 13 | private static final long serialVersionUID = 1238987802396038801L; 14 | 15 | private final int mWidth; 16 | private final int mHeight; 17 | private final String mUrl; 18 | 19 | public Image(final int width, final int height, final String url) { 20 | mWidth = width; 21 | mHeight = height; 22 | mUrl = url; 23 | } 24 | 25 | public int getWidth() { 26 | return mWidth; 27 | } 28 | 29 | public int getHeight() { 30 | return mHeight; 31 | } 32 | 33 | public String getUrl() { 34 | return mUrl; 35 | } 36 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/Post.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Date: 4/7/2014 9 | * Time: 5:29 PM 10 | * 11 | * @author MiG35 12 | */ 13 | public class Post implements Serializable, Comparable { 14 | 15 | private static final long serialVersionUID = -506449273621003026L; 16 | 17 | private final String mId; 18 | 19 | private final String mType; 20 | private final int mLikesCount; 21 | 22 | private final Image mThumbnailImage; 23 | private final Image mLowResolutionImage; 24 | private final Image mStandardResolutionImage; 25 | 26 | public Post(final String id, final String type, final int likesCount, final Image thumbnailImage, final Image lowResolutionImage, 27 | final Image standardResolutionImage) { 28 | mId = id; 29 | mType = type; 30 | mLikesCount = likesCount; 31 | mThumbnailImage = thumbnailImage; 32 | mLowResolutionImage = lowResolutionImage; 33 | mStandardResolutionImage = standardResolutionImage; 34 | } 35 | 36 | public String getId() { 37 | return mId; 38 | } 39 | 40 | public String getType() { 41 | return mType; 42 | } 43 | 44 | public int getLikesCount() { 45 | return mLikesCount; 46 | } 47 | 48 | public Image getThumbnailImage() { 49 | return mThumbnailImage; 50 | } 51 | 52 | public Image getLowResolutionImage() { 53 | return mLowResolutionImage; 54 | } 55 | 56 | public Image getStandardResolutionImage() { 57 | return mStandardResolutionImage; 58 | } 59 | 60 | @Override 61 | public int compareTo(@NonNull final Post another) { 62 | return mLikesCount < another.mLikesCount ? 1 : -1; 63 | } 64 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/User.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Date: 4/8/2014 9 | * Time: 12:38 PM 10 | * 11 | * @author MiG35 12 | */ 13 | public class User implements Serializable { 14 | 15 | private static final long serialVersionUID = 8758285803290081341L; 16 | 17 | @SerializedName("username") 18 | private String mNickName; 19 | @SerializedName("first_name") 20 | private String mFirstName; 21 | @SerializedName("last_name") 22 | private String mLastName; 23 | @SerializedName("profile_picture") 24 | private String mProfilePicture; 25 | @SerializedName("id") 26 | private String mId; 27 | 28 | public String getNickName() { 29 | return mNickName; 30 | } 31 | 32 | public String getFirstName() { 33 | return mFirstName; 34 | } 35 | 36 | public String getLastName() { 37 | return mLastName; 38 | } 39 | 40 | public String getProfilePicture() { 41 | return mProfilePicture; 42 | } 43 | 44 | public String getId() { 45 | return mId; 46 | } 47 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/results/UserDataResult.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data.results; 2 | 3 | import com.azoft.azoft.collage.data.User; 4 | 5 | /** 6 | * Date: 4/11/2014 7 | * Time: 3:55 PM 8 | * 9 | * @author MiG35 10 | */ 11 | public class UserDataResult { 12 | 13 | private final User mUser; 14 | private final boolean mDataViewAllowed; 15 | private final boolean mHasImages; 16 | 17 | private UserDataResult(final User user, final boolean dataViewAllowed, final boolean hasImages) { 18 | mUser = user; 19 | mDataViewAllowed = dataViewAllowed; 20 | mHasImages = hasImages; 21 | } 22 | 23 | public User getUser() { 24 | return mUser; 25 | } 26 | 27 | public boolean isDataViewAllowed() { 28 | return mDataViewAllowed; 29 | } 30 | 31 | public boolean isHasImages() { 32 | return mHasImages; 33 | } 34 | 35 | public static UserDataResult getNotAllowed(final User user) { 36 | return new UserDataResult(user, false, false); 37 | } 38 | 39 | public static UserDataResult getHasNoData(final User user) { 40 | return new UserDataResult(user, true, false); 41 | } 42 | 43 | public static UserDataResult getHasData(final User user) { 44 | return new UserDataResult(user, true, true); 45 | } 46 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/results/UserRecentFeedResult.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data.results; 2 | 3 | import com.azoft.azoft.collage.data.Post; 4 | 5 | import java.io.Serializable; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | /** 10 | * Date: 4/9/2014 11 | * Time: 9:31 AM 12 | * 13 | * @author MiG35 14 | */ 15 | public class UserRecentFeedResult implements Serializable { 16 | 17 | private static final long serialVersionUID = 4293075867718694860L; 18 | 19 | private final String mUserId; 20 | private final List mPosts; 21 | 22 | public UserRecentFeedResult(final String userId, final List posts) { 23 | if (null == posts) { 24 | throw new IllegalArgumentException("posts can't be null"); 25 | } 26 | mUserId = userId; 27 | mPosts = Collections.unmodifiableList(posts); 28 | } 29 | 30 | public String getUserId() { 31 | return mUserId; 32 | } 33 | 34 | public List getPosts() { 35 | return mPosts; 36 | } 37 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/data/results/UsersSearchResult.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.data.results; 2 | 3 | import com.azoft.azoft.collage.data.User; 4 | 5 | import java.io.Serializable; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * Date: 4/8/2014 12 | * Time: 1:04 PM 13 | * 14 | * @author MiG35 15 | */ 16 | public class UsersSearchResult implements Serializable { 17 | 18 | private static final long serialVersionUID = -7231458361668713668L; 19 | 20 | private final String mNickName; 21 | private final List mUsers; 22 | 23 | public UsersSearchResult(final String nickName, final List users) { 24 | mNickName = nickName; 25 | if (null == users) { 26 | mUsers = Collections.emptyList(); 27 | } else { 28 | mUsers = new ArrayList<>(users); 29 | } 30 | } 31 | 32 | public String getNickName() { 33 | return mNickName; 34 | } 35 | 36 | public List getUsers() { 37 | return mUsers; 38 | } 39 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/exceptions/CollageCreationException.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.exceptions; 2 | 3 | /** 4 | * Special exception for inner collage creation logic fails. 5 | *

6 | * Date: 4/9/2014 7 | * Time: 6:28 PM 8 | * 9 | * @author MiG35 10 | */ 11 | public class CollageCreationException extends NoHandleInBaseActivityException { 12 | 13 | private static final long serialVersionUID = -8353694528536973759L; 14 | 15 | public CollageCreationException() { 16 | } 17 | 18 | public CollageCreationException(final String detailMessage) { 19 | super(detailMessage); 20 | } 21 | 22 | public CollageCreationException(final String detailMessage, final Throwable throwable) { 23 | super(detailMessage, throwable); 24 | } 25 | 26 | public CollageCreationException(final Throwable throwable) { 27 | super(throwable); 28 | } 29 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/exceptions/DiskWriteException.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.exceptions; 2 | 3 | /** 4 | * Indicates some disk write/read errors. 5 | *

6 | * Date: 4/9/2014 7 | * Time: 12:29 PM 8 | * 9 | * @author MiG35 10 | */ 11 | public class DiskWriteException extends Exception { 12 | 13 | private static final long serialVersionUID = -7962369698722370033L; 14 | 15 | public DiskWriteException() { 16 | } 17 | 18 | public DiskWriteException(final Throwable throwable) { 19 | super(throwable); 20 | } 21 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/exceptions/InternalServerException.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.exceptions; 2 | 3 | /** 4 | * Some exception in protocol or in server communication. 5 | *

6 | * Date: 4/8/2014 7 | * Time: 2:49 PM 8 | * 9 | * @author MiG35 10 | */ 11 | public class InternalServerException extends Exception { 12 | 13 | private static final long serialVersionUID = 5180367710900570152L; 14 | 15 | public InternalServerException() { 16 | } 17 | 18 | public InternalServerException(final String detailMessage) { 19 | super(detailMessage); 20 | } 21 | 22 | public InternalServerException(final String detailMessage, final Throwable throwable) { 23 | super(detailMessage, throwable); 24 | } 25 | 26 | public InternalServerException(final Throwable throwable) { 27 | super(throwable); 28 | } 29 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/exceptions/NoHandleInBaseActivityException.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.exceptions; 2 | 3 | /** 4 | * Indicates that this exception should be ignored by base activity exception handle. 5 | *

6 | * Date: 4/10/2014 7 | * Time: 1:02 PM 8 | * 9 | * @author MiG35 10 | */ 11 | public class NoHandleInBaseActivityException extends Exception { 12 | 13 | private static final long serialVersionUID = 2439717201384478829L; 14 | 15 | public NoHandleInBaseActivityException() { 16 | } 17 | 18 | public NoHandleInBaseActivityException(final String detailMessage) { 19 | super(detailMessage); 20 | } 21 | 22 | public NoHandleInBaseActivityException(final String detailMessage, final Throwable throwable) { 23 | super(detailMessage, throwable); 24 | } 25 | 26 | public NoHandleInBaseActivityException(final Throwable throwable) { 27 | super(throwable); 28 | } 29 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/exceptions/NotAllowedException.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.exceptions; 2 | 3 | /** 4 | * Indicates that our app is not allowed to access some data. 5 | *

6 | * Date: 4/9/2014 7 | * Time: 11:16 AM 8 | * 9 | * @author MiG35 10 | */ 11 | public class NotAllowedException extends InternalServerException { 12 | 13 | private static final long serialVersionUID = -4873834503540566594L; 14 | 15 | public NotAllowedException() { 16 | } 17 | 18 | public NotAllowedException(final String detailMessage) { 19 | super(detailMessage); 20 | } 21 | 22 | public NotAllowedException(final String message, final Throwable cause) { 23 | super(message, cause); 24 | } 25 | 26 | public NotAllowedException(final Throwable cause) { 27 | super(cause); 28 | } 29 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/loaders/CollageBigImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.loaders; 2 | 3 | import android.content.Context; 4 | 5 | import com.mig35.loaderlib.loaders.DataAsyncTaskLibLoader; 6 | import com.azoft.azoft.collage.data.CollageRegionData; 7 | import com.azoft.azoft.collage.data.Post; 8 | import com.azoft.azoft.collage.exceptions.DiskWriteException; 9 | import com.azoft.azoft.collage.exceptions.InternalServerException; 10 | import com.azoft.azoft.collage.utils.CollageRegion; 11 | import com.azoft.azoft.collage.utils.CommonUtils; 12 | 13 | import java.io.File; 14 | import java.io.FileOutputStream; 15 | import java.net.HttpURLConnection; 16 | import java.net.URL; 17 | 18 | /** 19 | * Will load big image for collage item from the Internet. 20 | * Stores it to external or internal cache dir. 21 | *

22 | * Date: 4/9/2014 23 | * Time: 12:08 PM 24 | * 25 | * @author MiG35 26 | */ 27 | public class CollageBigImageLoader extends DataAsyncTaskLibLoader { 28 | 29 | private static final String IMAGE_NAME = "inst_img_%d.jpg"; 30 | 31 | private final CollageRegion mCollageRegion; 32 | private final Post mPost; 33 | 34 | public CollageBigImageLoader(final Context context, final CollageRegion collageRegion, final Post post) { 35 | super(context); 36 | if (null == collageRegion || null == post) { 37 | throw new IllegalArgumentException("collageRegion and post can't be null"); 38 | } 39 | 40 | mCollageRegion = collageRegion; 41 | mPost = post; 42 | } 43 | 44 | @Override 45 | protected CollageRegionData performLoad() throws Exception { 46 | final File filesDir = CommonUtils.getCacheFileDir(); 47 | if (null == filesDir) { 48 | throw new DiskWriteException(); 49 | } 50 | final File outputFile = new File(filesDir, String.format(IMAGE_NAME, mCollageRegion.getId())); 51 | if (outputFile.exists() && !outputFile.delete()) { 52 | throw new DiskWriteException(); 53 | } 54 | 55 | final HttpURLConnection connection = (HttpURLConnection) new URL(mPost.getStandardResolutionImage().getUrl()).openConnection(); 56 | 57 | connection.setUseCaches(true); 58 | final int responseCode = connection.getResponseCode(); 59 | if (responseCode >= 300) { 60 | connection.disconnect(); 61 | throw new InternalServerException(); 62 | } 63 | 64 | CommonUtils.writeNetworkStreamToAnOtherStream(connection.getInputStream(), new FileOutputStream(outputFile)); 65 | connection.disconnect(); 66 | 67 | if (outputFile.exists()) { 68 | return new CollageRegionData(outputFile); 69 | } else { 70 | throw new DiskWriteException(); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/loaders/CollagePreviewCreatorLoader.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.loaders; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | 8 | import com.azoft.azoft.collage.R; 9 | import com.azoft.azoft.collage.data.CollageFillData; 10 | import com.azoft.azoft.collage.data.CollageRegionData; 11 | import com.azoft.azoft.collage.exceptions.CollageCreationException; 12 | import com.azoft.azoft.collage.utils.CollageRegion; 13 | import com.azoft.azoft.collage.utils.MediaUtils; 14 | import com.mig35.loaderlib.loaders.DataAsyncTaskLibLoader; 15 | 16 | import java.io.File; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Construct result image. And store it to internal directory and return its path in FileProvider. 22 | *

23 | * Date: 4/9/2014 24 | * Time: 6:19 PM 25 | * 26 | * @author MiG35 27 | */ 28 | public class CollagePreviewCreatorLoader extends DataAsyncTaskLibLoader { 29 | 30 | private final CollageFillData mCollageFillData; 31 | 32 | public CollagePreviewCreatorLoader(final Context context, final CollageFillData collageFillData) { 33 | super(context); 34 | if (null == collageFillData) { 35 | throw new IllegalArgumentException("collageFillData can't be null"); 36 | } 37 | 38 | mCollageFillData = collageFillData; 39 | } 40 | 41 | @Override 42 | protected String performLoad() throws Exception { 43 | final Map collageDataMap = new HashMap<>(); 44 | 45 | int maxSize = 0; // we need to know our result image size (as biggest image size). 46 | for (final CollageRegion collageRegion : mCollageFillData.getCollageRegions()) { 47 | final CollageRegionData regionData = mCollageFillData.getRegionData(collageRegion); 48 | maxSize = Math.max(maxSize, getImageSize(regionData.getImageFile())); 49 | collageDataMap.put(collageRegion, regionData); 50 | } 51 | 52 | int sampleSize = 1; 53 | Bitmap outBitmap = null; 54 | do { 55 | try { 56 | outBitmap = Bitmap.createBitmap(maxSize / sampleSize, maxSize / sampleSize, Bitmap.Config.ARGB_8888); 57 | 58 | final Canvas canvas = new Canvas(outBitmap); 59 | canvas.drawColor(getContext().getResources().getColor(R.color.collage_bg_color)); 60 | 61 | for (final Map.Entry entryItem : collageDataMap.entrySet()) { 62 | drawCollageRegionOnCanvas(canvas, entryItem.getKey(), entryItem.getValue()); 63 | } 64 | break; 65 | } catch (final OutOfMemoryError thr) { 66 | sampleSize *= 2; 67 | if (null != outBitmap) { 68 | outBitmap.recycle(); 69 | } 70 | outBitmap = null; 71 | } 72 | } while (sampleSize < 100); 73 | 74 | if (null == outBitmap) { 75 | throw new CollageCreationException(new OutOfMemoryError()); 76 | } 77 | try { 78 | return MediaUtils.insertImage(outBitmap, getContext().getString(R.string.text_image_collage_preview)); 79 | } finally { 80 | outBitmap.recycle(); 81 | } 82 | } 83 | 84 | private void drawCollageRegionOnCanvas(final Canvas canvas, final CollageRegion collageRegion, final CollageRegionData collageRegionData) throws CollageCreationException { 85 | for (int sampleSize = 1; sampleSize < 100; sampleSize *= 2) { 86 | try { 87 | drawCollageRegionOnCanvasOld(canvas, collageRegion, collageRegionData, sampleSize); 88 | return; 89 | } catch (final OutOfMemoryError error) { 90 | // pass 91 | } 92 | } 93 | throw new OutOfMemoryError(); 94 | } 95 | 96 | private void drawCollageRegionOnCanvasOld(final Canvas canvas, final CollageRegion collageRegion, final CollageRegionData collageRegionData, final int sampleSize) 97 | throws CollageCreationException { 98 | // region visible width and height 99 | final int regionWidth = (int) (canvas.getWidth() * collageRegion.getWidth()); 100 | final int regionHeight = (int) (canvas.getWidth() * collageRegion.getHeight()); 101 | 102 | final BitmapFactory.Options options = new BitmapFactory.Options(); 103 | options.inSampleSize = sampleSize; 104 | final Bitmap regionDecodedBitmap = BitmapFactory.decodeFile(collageRegionData.getImageFile().getAbsolutePath(), options); 105 | if (null == regionDecodedBitmap) { 106 | throw new CollageCreationException(); 107 | } 108 | final Bitmap memoryOptimizedDecodedBitmap; 109 | if (regionDecodedBitmap.getWidth() == regionWidth && regionDecodedBitmap.getHeight() == regionHeight) { 110 | // decoded bitmap is the same as our region. nothing to do more 111 | memoryOptimizedDecodedBitmap = regionDecodedBitmap; 112 | } else { 113 | // we should make our decoded bitmap scaled for region. because region may be not square we use it's max size 114 | final float imageScale = Math.max(1f * regionWidth / regionDecodedBitmap.getWidth(), 1f * regionHeight / regionDecodedBitmap.getHeight()); 115 | final Bitmap tmp = Bitmap.createScaledBitmap(regionDecodedBitmap, Math.round(imageScale * regionDecodedBitmap.getWidth()), Math 116 | .round(imageScale * regionDecodedBitmap.getHeight()), true); 117 | if (tmp != regionDecodedBitmap) { 118 | regionDecodedBitmap.recycle(); 119 | } 120 | if (null == tmp) { 121 | memoryOptimizedDecodedBitmap = null; 122 | } else { 123 | memoryOptimizedDecodedBitmap = tmp; 124 | } 125 | } 126 | if (null == memoryOptimizedDecodedBitmap) { 127 | throw new CollageCreationException(); 128 | } 129 | final float scale = collageRegionData.getImageScale(); 130 | final float left = collageRegionData.getImageLeft() == null ? 0 : collageRegionData.getImageLeft(); 131 | final float top = collageRegionData.getImageTop() == null ? 0 : collageRegionData.getImageTop(); 132 | 133 | // we should crop image to be exactly as our scaled region (then we should scale it upper) 134 | final Bitmap scaledResultBitmap = 135 | Bitmap.createBitmap(memoryOptimizedDecodedBitmap, (int) (left * (memoryOptimizedDecodedBitmap.getWidth() - regionWidth / scale)), 136 | (int) (top * (memoryOptimizedDecodedBitmap.getHeight() - regionHeight / scale)), (int) (regionWidth / scale), 137 | (int) (regionHeight / scale)); 138 | if (scaledResultBitmap != memoryOptimizedDecodedBitmap) { 139 | memoryOptimizedDecodedBitmap.recycle(); 140 | } 141 | final Bitmap resultBitmap = Bitmap.createScaledBitmap(scaledResultBitmap, regionWidth, regionHeight, true); 142 | if (resultBitmap != scaledResultBitmap) { 143 | scaledResultBitmap.recycle(); 144 | } 145 | 146 | canvas.drawBitmap(resultBitmap, (int) (collageRegion.getLeft() * canvas.getWidth()), (int) (collageRegion.getTop() * canvas.getHeight()), 147 | null); 148 | resultBitmap.recycle(); 149 | } 150 | 151 | private int getImageSize(final File imageFile) { 152 | final BitmapFactory.Options options = new BitmapFactory.Options(); 153 | options.inJustDecodeBounds = true; 154 | BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); 155 | return Math.max(options.outWidth, options.outHeight); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/loaders/UserDataLoader.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.loaders; 2 | 3 | import android.content.Context; 4 | 5 | import com.mig35.loaderlib.loaders.DataAsyncTaskLibLoader; 6 | import com.azoft.azoft.collage.data.User; 7 | import com.azoft.azoft.collage.data.results.UserDataResult; 8 | import com.azoft.azoft.collage.exceptions.InternalServerException; 9 | import com.azoft.azoft.collage.server.RetrofitHelper; 10 | import com.azoft.azoft.collage.server.ServerConnector; 11 | import com.azoft.azoft.collage.server.responces.UserRecentFeedResponse; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * Date: 4/11/2014 17 | * Time: 3:54 PM 18 | * 19 | * @author MiG35 20 | */ 21 | public class UserDataLoader extends DataAsyncTaskLibLoader { 22 | 23 | private final User mUser; 24 | private final String mAccessToken; 25 | 26 | public UserDataLoader(final Context context, final User user, final String accessToken) { 27 | super(context); 28 | 29 | mUser = user; 30 | mAccessToken = accessToken; 31 | } 32 | 33 | @Override 34 | protected UserDataResult performLoad() throws Exception { 35 | try { 36 | final UserRecentFeedResponse response = ServerConnector.getInstagramService().getUserRecentFeed(mUser.getId(), 1, mAccessToken); 37 | if (null == response) { 38 | throw new InternalServerException("Response is null"); 39 | } 40 | final List postItems = response.getPostItems(); 41 | if (null == postItems || postItems.isEmpty()) { 42 | return UserDataResult.getHasNoData(mUser); 43 | } else { 44 | return UserDataResult.getHasData(mUser); 45 | } 46 | } catch (final InternalServerException e) { 47 | // we should check is we don't have access to view user's posts. if so, we shouldn't try to load again 48 | if (RetrofitHelper.isAccessDeniedException(e)) { 49 | return UserDataResult.getNotAllowed(mUser); 50 | } 51 | throw e; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/loaders/UserImageFeedLoader.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.loaders; 2 | 3 | import android.content.Context; 4 | 5 | import com.mig35.loaderlib.exceptions.NoNetworkException; 6 | import com.mig35.loaderlib.loaders.DataAsyncTaskLibLoader; 7 | import com.azoft.azoft.collage.data.Image; 8 | import com.azoft.azoft.collage.data.Post; 9 | import com.azoft.azoft.collage.data.results.UserRecentFeedResult; 10 | import com.azoft.azoft.collage.exceptions.InternalServerException; 11 | import com.azoft.azoft.collage.exceptions.NotAllowedException; 12 | import com.azoft.azoft.collage.server.RetrofitHelper; 13 | import com.azoft.azoft.collage.server.ServerConnector; 14 | import com.azoft.azoft.collage.server.responces.UserRecentFeedResponse; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | /** 21 | * Will load last user's images posts. Has tries and feeds count parameters as static in this class. 22 | *

23 | * Date: 4/9/2014 24 | * Time: 9:29 AM 25 | * 26 | * @author MiG35 27 | */ 28 | public class UserImageFeedLoader extends DataAsyncTaskLibLoader { 29 | 30 | private static final int TRIES_COUNT = 3; 31 | private static final int FEED_COUNT = 100; 32 | private static final String IMAGE_POST_TYPE_STRING = "image"; 33 | 34 | private final String mUserId; 35 | private final String mAccessToken; 36 | 37 | public UserImageFeedLoader(final Context context, final String userId, final String accessToken) { 38 | super(context); 39 | 40 | mUserId = userId; 41 | mAccessToken = accessToken; 42 | } 43 | 44 | @Override 45 | protected UserRecentFeedResult performLoad() throws Exception { 46 | final List posts = new ArrayList<>(); 47 | loadPosts(posts); 48 | Collections.sort(posts); 49 | return new UserRecentFeedResult(mUserId, posts); 50 | } 51 | 52 | private void loadPosts(final List posts) throws Exception { 53 | String currentMaxId = null; 54 | 55 | do { 56 | final UserRecentFeedResponse response = getUserRecentFeedResponse(currentMaxId); 57 | 58 | final UserRecentFeedResponse.Pagination pagination = response.getPagination(); 59 | final List postItems = response.getPostItems(); 60 | if (null != postItems) { 61 | for (final UserRecentFeedResponse.PostItem postItem : postItems) { 62 | if (IMAGE_POST_TYPE_STRING.equals(postItem.getType())) { 63 | final Post post = convertToPost(postItem); 64 | if (null != post && posts.size() < FEED_COUNT) { 65 | posts.add(post); 66 | } 67 | } 68 | } 69 | } 70 | currentMaxId = null == pagination ? null : pagination.getNextMaxId(); 71 | } while (posts.size() < FEED_COUNT && null != currentMaxId); 72 | } 73 | 74 | private UserRecentFeedResponse getUserRecentFeedResponse(final String currentMaxId) throws Exception { 75 | Exception exception = new InternalServerException("Response is null"); 76 | // because some times Internet is not good we should try to load this data some times, because this data is important. 77 | for (int i = 0; i < TRIES_COUNT; ++i) { 78 | try { 79 | final UserRecentFeedResponse response; 80 | if (null == currentMaxId) { 81 | response = ServerConnector.getInstagramService().getUserRecentFeed(mUserId, FEED_COUNT, mAccessToken); 82 | } else { 83 | response = ServerConnector.getInstagramService().getUserRecentFeed(mUserId, FEED_COUNT, currentMaxId, mAccessToken); 84 | } 85 | if (null != response) { 86 | return response; 87 | } 88 | } catch (final NoNetworkException e) { 89 | exception = e; 90 | } catch (final InternalServerException e) { 91 | exception = e; 92 | // we should check is we don't have access to view user's posts. if so, we shouldn't try to load again 93 | if (RetrofitHelper.isAccessDeniedException(e)) { 94 | throw new NotAllowedException(e); 95 | } 96 | } 97 | } 98 | // we have an error. throw it! 99 | throw exception; 100 | } 101 | 102 | private Post convertToPost(final UserRecentFeedResponse.PostItem postItem) { 103 | final UserRecentFeedResponse.PostImages postItemImages = postItem.getPostImages(); 104 | if (null == postItemImages || !postItemImages.hasAllImages() || !postItemImages.hasDataInAllImages()) { 105 | // we need only images posts 106 | return null; 107 | } 108 | final int likesCount = null == postItem.getLikes() ? 0 : postItem.getLikes().getCount(); 109 | 110 | return new Post(postItem.getId(), postItem.getType(), likesCount, convertToImage(postItemImages.getThumbnailImage()), 111 | convertToImage(postItemImages.getLowResolutionImage()), convertToImage(postItemImages.getStandardResolutionImage())); 112 | } 113 | 114 | private Image convertToImage(final UserRecentFeedResponse.PostImage postImage) { 115 | if (null == postImage) { 116 | return null; 117 | } 118 | 119 | return new Image(postImage.getWidth(), postImage.getHeight(), postImage.getUrl()); 120 | } 121 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/loaders/UsersSearchLoader.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.loaders; 2 | 3 | import android.content.Context; 4 | 5 | import com.mig35.loaderlib.loaders.DataAsyncTaskLibLoader; 6 | import com.azoft.azoft.collage.data.results.UsersSearchResult; 7 | import com.azoft.azoft.collage.exceptions.InternalServerException; 8 | import com.azoft.azoft.collage.server.ServerConnector; 9 | import com.azoft.azoft.collage.server.responces.SearchUserResponse; 10 | 11 | /** 12 | * Will load users with parameter nickName 13 | *

14 | * Date: 4/8/2014 15 | * Time: 11:39 AM 16 | * 17 | * @author MiG35 18 | */ 19 | public class UsersSearchLoader extends DataAsyncTaskLibLoader { 20 | 21 | private final String mNickName; 22 | private final String mAccessToken; 23 | 24 | public UsersSearchLoader(final Context context, final String nickName, final String accessToken) { 25 | super(context); 26 | 27 | mNickName = nickName; 28 | mAccessToken = accessToken; 29 | } 30 | 31 | @Override 32 | protected UsersSearchResult performLoad() throws Exception { 33 | final SearchUserResponse response = ServerConnector.getInstagramService().searchUser(mNickName, mAccessToken); 34 | if (null == response) { 35 | throw new InternalServerException("Response is null"); 36 | } 37 | return new UsersSearchResult(mNickName, response.getUsers()); 38 | } 39 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/server/InstagramService.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.server; 2 | 3 | import com.azoft.azoft.collage.server.responces.SearchUserResponse; 4 | import com.azoft.azoft.collage.server.responces.UserRecentFeedResponse; 5 | 6 | import retrofit.http.GET; 7 | import retrofit.http.Path; 8 | import retrofit.http.Query; 9 | 10 | /** 11 | * Date: 4/8/2014 12 | * Time: 2:28 PM 13 | * 14 | * @author MiG35 15 | */ 16 | @SuppressWarnings("InterfaceNeverImplemented") 17 | public interface InstagramService { 18 | 19 | @GET("/users/search") 20 | SearchUserResponse searchUser(@Query("q") final String query, @Query("access_token") final String accessToken) throws Exception; 21 | 22 | @GET("/users/{user-id}/media/recent") 23 | UserRecentFeedResponse getUserRecentFeed(@Path("user-id") final String userId, @Query("count") final int count, @Query("access_token") final String accessToken) throws Exception; 24 | 25 | @GET("/users/{user-id}/media/recent") 26 | UserRecentFeedResponse getUserRecentFeed(@Path("user-id") final String userId, @Query("count") final int count, 27 | @Query("max_id") final String maxId, @Query("access_token") final String accessToken) throws Exception; 28 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/server/RetrofitHelper.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.server; 2 | 3 | import com.azoft.azoft.collage.exceptions.InternalServerException; 4 | 5 | import retrofit.RetrofitError; 6 | import retrofit.client.Response; 7 | 8 | /** 9 | * Date: 4/11/2014 10 | * Time: 3:57 PM 11 | * 12 | * @author MiG35 13 | */ 14 | public final class RetrofitHelper { 15 | 16 | private static final int NOT_ALLOWED = 400; 17 | 18 | private RetrofitHelper() { 19 | } 20 | 21 | public static boolean isAccessDeniedException(final InternalServerException exception) { 22 | if (null == exception) { 23 | return false; 24 | } 25 | // we should check is we don't have access to view user's posts. if so, we shouldn't try to load again 26 | final Throwable cause = exception.getCause(); 27 | if (cause instanceof RetrofitError) { 28 | final Response response = ((RetrofitError) cause).getResponse(); 29 | if (null != response && response.getStatus() == NOT_ALLOWED) { 30 | return true; 31 | } 32 | } 33 | return false; 34 | } 35 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/server/ServerConnector.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.server; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | import com.mig35.loaderlib.exceptions.NoNetworkException; 7 | import com.azoft.azoft.collage.exceptions.InternalServerException; 8 | import com.squareup.okhttp.OkHttpClient; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import retrofit.ErrorHandler; 13 | import retrofit.RestAdapter; 14 | import retrofit.RetrofitError; 15 | import retrofit.converter.GsonConverter; 16 | 17 | /** 18 | * Contains all service components such as Instgram and general Http Client. Thread safe singleton. 19 | *

20 | * Date: 6/28/13 21 | * Time: 7:24 PM 22 | * 23 | * @author MiG35 24 | */ 25 | public final class ServerConnector { 26 | 27 | private static final String SERVER_URL = "https://api.instagram.com"; 28 | private static final String VERSION = "/v1"; 29 | private static final String SERVER_URL_WITH_VERSION = SERVER_URL + VERSION; 30 | 31 | private static final long TIMEOUT_TIME = 10; 32 | 33 | private final InstagramService mInstagramService; 34 | private final OkHttpClient mOkHttpClient; 35 | 36 | private ServerConnector() { 37 | final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create(); 38 | 39 | final RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(SERVER_URL_WITH_VERSION).setConverter(new GsonConverter(gson)) 40 | .setErrorHandler(new ErrorHandler() { 41 | @Override 42 | public Throwable handleError(final RetrofitError cause) { 43 | if (RetrofitError.Kind.NETWORK == cause.getKind()) { 44 | return new NoNetworkException(); 45 | } else { 46 | return new InternalServerException(cause); 47 | } 48 | } 49 | }).build(); 50 | 51 | mInstagramService = restAdapter.create(InstagramService.class); 52 | 53 | mOkHttpClient = new OkHttpClient(); 54 | mOkHttpClient.setConnectTimeout(TIMEOUT_TIME, TimeUnit.SECONDS); 55 | mOkHttpClient.setReadTimeout(TIMEOUT_TIME, TimeUnit.SECONDS); 56 | } 57 | 58 | public static InstagramService getInstagramService() { 59 | return Holder.SERVER_CONNECTOR.mInstagramService; 60 | } 61 | 62 | public static OkHttpClient getOkHttpClient() { 63 | return Holder.SERVER_CONNECTOR.mOkHttpClient; 64 | } 65 | 66 | private static final class Holder { 67 | 68 | private static final ServerConnector SERVER_CONNECTOR = new ServerConnector(); 69 | } 70 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/server/responces/SearchUserResponse.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.server.responces; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.azoft.azoft.collage.data.User; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Date: 4/8/2014 10 | * Time: 2:38 PM 11 | * 12 | * @author MiG35 13 | */ 14 | public class SearchUserResponse { 15 | 16 | @SerializedName("data") 17 | private List mUsers; 18 | 19 | public List getUsers() { 20 | return mUsers; 21 | } 22 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/server/responces/UserRecentFeedResponse.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.server.responces; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Date: 4/9/2014 9 | * Time: 9:34 AM 10 | * 11 | * @author MiG35 12 | */ 13 | public class UserRecentFeedResponse { 14 | 15 | @SerializedName("pagination") 16 | private Pagination mPagination; 17 | @SerializedName("data") 18 | private List mPostItems; 19 | 20 | public Pagination getPagination() { 21 | return mPagination; 22 | } 23 | 24 | public List getPostItems() { 25 | return mPostItems; 26 | } 27 | 28 | public static class Pagination { 29 | 30 | @SerializedName("next_url") 31 | private String mNextUrl; 32 | @SerializedName("next_max_id") 33 | private String mNextMaxId; 34 | 35 | public String getNextUrl() { 36 | return mNextUrl; 37 | } 38 | 39 | public String getNextMaxId() { 40 | return mNextMaxId; 41 | } 42 | } 43 | 44 | public static class PostItem { 45 | 46 | @SerializedName("type") 47 | private String mType; 48 | @SerializedName("id") 49 | private String mId; 50 | @SerializedName("likes") 51 | private Likes mLikes; 52 | @SerializedName("images") 53 | private PostImages mPostImages; 54 | 55 | public String getType() { 56 | return mType; 57 | } 58 | 59 | public String getId() { 60 | return mId; 61 | } 62 | 63 | public PostImages getPostImages() { 64 | return mPostImages; 65 | } 66 | 67 | public Likes getLikes() { 68 | return mLikes; 69 | } 70 | } 71 | 72 | public static class Likes { 73 | 74 | @SerializedName("count") 75 | private int mCount; 76 | 77 | public int getCount() { 78 | return mCount; 79 | } 80 | } 81 | 82 | public static class PostImages { 83 | 84 | @SerializedName("low_resolution") 85 | private PostImage mLowResolutionImage; 86 | @SerializedName("thumbnail") 87 | private PostImage mThumbnailImage; 88 | @SerializedName("standard_resolution") 89 | private PostImage mStandardResolutionImage; 90 | 91 | public PostImage getLowResolutionImage() { 92 | return mLowResolutionImage; 93 | } 94 | 95 | public PostImage getThumbnailImage() { 96 | return mThumbnailImage; 97 | } 98 | 99 | public PostImage getStandardResolutionImage() { 100 | return mStandardResolutionImage; 101 | } 102 | 103 | public boolean hasAllImages() { 104 | return null != mLowResolutionImage && null != mThumbnailImage && null != mStandardResolutionImage; 105 | } 106 | 107 | public boolean hasDataInAllImages() { 108 | return mLowResolutionImage.hasData() && mThumbnailImage.hasData() && mStandardResolutionImage.hasData(); 109 | } 110 | } 111 | 112 | public static class PostImage { 113 | 114 | @SerializedName("url") 115 | private String mUrl; 116 | @SerializedName("width") 117 | private int mWidth; 118 | @SerializedName("height") 119 | private int mHeight; 120 | 121 | public String getUrl() { 122 | return mUrl; 123 | } 124 | 125 | public int getWidth() { 126 | return mWidth; 127 | } 128 | 129 | public int getHeight() { 130 | return mHeight; 131 | } 132 | 133 | public boolean hasData() { 134 | return 0 != mWidth && 0 != mHeight && null != mUrl; 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/CollageActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.Window; 6 | 7 | import com.azoft.azoft.collage.R; 8 | import com.azoft.azoft.collage.app.CollageApplication; 9 | import com.azoft.azoft.collage.exceptions.DiskWriteException; 10 | import com.azoft.azoft.collage.exceptions.NoHandleInBaseActivityException; 11 | import com.azoft.azoft.collage.exceptions.NotAllowedException; 12 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 13 | import com.mig35.injectorlib.utils.inject.Injector; 14 | import com.mig35.loaderlib.exceptions.NoNetworkException; 15 | import com.mig35.loaderlib.utils.ActivityLoaderHelper; 16 | import com.mig35.loaderlib.utils.ActivityLoaderListener; 17 | import com.mig35.loaderlib.utils.FragmentToActivityLoaderTaskListener; 18 | import com.mig35.loaderlib.utils.LoaderHelper; 19 | 20 | /** 21 | * Base Activity class with action bar. 22 | *

23 | * Date: 4/8/2014 24 | * Time: 11:01 AM 25 | * 26 | * @author MiG35 27 | */ 28 | public abstract class CollageActivity extends AppCompatActivity implements ActivityLoaderListener { 29 | 30 | private Injector mInjector; 31 | 32 | @InjectSavedState 33 | private ActivityLoaderHelper mActivityLoaderHelper; 34 | 35 | @Override 36 | protected void onCreate(final Bundle savedInstanceState) { 37 | mInjector = Injector.init(this); 38 | 39 | mInjector.applyOnActivityCreate(this, savedInstanceState); 40 | 41 | if (null == mActivityLoaderHelper) { 42 | mActivityLoaderHelper = new ActivityLoaderHelper(); 43 | } 44 | 45 | super.onCreate(savedInstanceState); 46 | 47 | mActivityLoaderHelper.onCreate(this); 48 | 49 | supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 50 | } 51 | 52 | @Override 53 | public void onContentChanged() { 54 | super.onContentChanged(); 55 | 56 | mInjector.applyOnActivityContentChange(this); 57 | } 58 | 59 | @Override 60 | protected void onStart() { 61 | mActivityLoaderHelper.onStart(); 62 | 63 | super.onStart(); 64 | } 65 | 66 | @Override 67 | protected void onResumeFragments() { 68 | super.onResumeFragments(); 69 | 70 | mActivityLoaderHelper.onResumeFragments(); 71 | } 72 | 73 | @Override 74 | protected void onDestroy() { 75 | super.onDestroy(); 76 | 77 | mActivityLoaderHelper.onDestroy(); 78 | mInjector.applyOnActivityDestroy(this); 79 | } 80 | 81 | @Override 82 | protected void onSaveInstanceState(final Bundle outState) { 83 | super.onSaveInstanceState(outState); 84 | 85 | mActivityLoaderHelper.onSaveInstanceState(); 86 | mInjector.applyOnActivitySaveInstanceState(this, outState); 87 | } 88 | 89 | @Override 90 | protected void onPause() { 91 | super.onPause(); 92 | 93 | mActivityLoaderHelper.onPause(); 94 | } 95 | 96 | @Override 97 | protected void onStop() { 98 | super.onStop(); 99 | 100 | mActivityLoaderHelper.onStop(); 101 | } 102 | 103 | @Override 104 | public void showProgress(final boolean hasRunningLoaders) { 105 | // nothing to see here 106 | } 107 | 108 | @Override 109 | public void onLoaderResult(final int id, final Object result) { 110 | // nothing to see here 111 | } 112 | 113 | @Override 114 | public void onLoaderError(final int id, final Exception exception) { 115 | // general base activity exception handle 116 | if (exception instanceof NoNetworkException) { 117 | showToast(R.string.error_no_network); 118 | } else if (exception instanceof NotAllowedException) { 119 | showToast(R.string.error_not_allowed); 120 | } else if (exception instanceof DiskWriteException) { 121 | showToast(R.string.error_disk_error); 122 | } else if (!(exception instanceof NoHandleInBaseActivityException)) { 123 | showToast(R.string.error_unknown); 124 | } 125 | } 126 | 127 | public void showToast(final int toastMessage, final int gravity) { 128 | showToast(getString(toastMessage), gravity); 129 | } 130 | 131 | public void showToast(final int toastMessage) { 132 | showToast(getString(toastMessage)); 133 | } 134 | 135 | public void showToast(final String message) { 136 | if (null == message) { 137 | return; 138 | } 139 | 140 | CollageApplication.getInstance().showToast(message, null); 141 | } 142 | 143 | private void showToast(final String message, final int gravity) { 144 | if (null == message) { 145 | return; 146 | } 147 | 148 | CollageApplication.getInstance().showToast(message, gravity); 149 | } 150 | 151 | @Override 152 | public LoaderHelper getLoaderHelper() { 153 | return mActivityLoaderHelper; 154 | } 155 | 156 | @Override 157 | public void addLoaderListener(final FragmentToActivityLoaderTaskListener loaderTaskListener) { 158 | mActivityLoaderHelper.addLoaderListener(loaderTaskListener); 159 | } 160 | 161 | @Override 162 | public void removeLoaderListener(final FragmentToActivityLoaderTaskListener loaderFragment) { 163 | mActivityLoaderHelper.removeLoaderListener(loaderFragment); 164 | } 165 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/CollageAuthActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.view.MenuItem; 8 | 9 | public abstract class CollageAuthActivity extends CollageActivity { 10 | 11 | private static final String EXTRA_ACCESS_TOKEN = "com.azoft.azoft.collage.ui.activities.phone.CollageAuthActivity.EXTRA_ACCESS_TOKEN"; 12 | 13 | public static Intent createIntent(final Context context, final Class activityClass, final String accessToken) { 14 | return new Intent(context, activityClass).putExtra(EXTRA_ACCESS_TOKEN, accessToken); 15 | } 16 | 17 | public static Intent createIntent(final CollageAuthActivity collageAuthActivity, final Class activityClass) { 18 | return new Intent(collageAuthActivity, activityClass).putExtra(EXTRA_ACCESS_TOKEN, collageAuthActivity.getIntent().getStringExtra(EXTRA_ACCESS_TOKEN)); 19 | } 20 | 21 | protected String mAccessToken; 22 | 23 | @Override 24 | protected final void onCreate(final Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | mAccessToken = getIntent().getStringExtra(EXTRA_ACCESS_TOKEN); 28 | if (null == mAccessToken) { 29 | finish(); 30 | } else { 31 | onCreate(savedInstanceState, mAccessToken); 32 | } 33 | } 34 | 35 | public String getAccessToken() { 36 | return mAccessToken; 37 | } 38 | 39 | @Override 40 | public boolean onOptionsItemSelected(final MenuItem item) { 41 | if (item.getItemId() == android.R.id.home) { 42 | // should return to start activity and clear all others 43 | final Intent intent = CollageAuthActivity.createIntent(this, StartActivity.class); 44 | 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 46 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 47 | } else { 48 | intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 49 | } 50 | 51 | startActivity(intent); 52 | return true; 53 | } 54 | return super.onOptionsItemSelected(item); 55 | } 56 | 57 | protected abstract void onCreate(final Bundle savedInstanceState, final String accessToken); 58 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/CollageBuilderActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | import com.azoft.azoft.collage.data.Collage; 6 | import com.azoft.azoft.collage.data.User; 7 | import com.azoft.azoft.collage.ui.fragments.CollageBuilderFragment; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * Date: 4/8/2014 13 | * Time: 6:38 PM 14 | * 15 | * @author MiG35 16 | */ 17 | public class CollageBuilderActivity extends SingleFragmentActivity { 18 | 19 | public static final String EXTRA_USER = "com.redmadrobot.azoft.collage.ui.activities.phone.CollageBuilderActivity.EXTRA_USER"; 20 | public static final String EXTRA_KOLAJ = "com.redmadrobot.azoft.collage.ui.activities.phone.CollageBuilderActivity.EXTRA_KOLAJ"; 21 | 22 | @Override 23 | protected Fragment createFragment() { 24 | final Serializable user = getIntent().getSerializableExtra(EXTRA_USER); 25 | final Serializable collage = getIntent().getSerializableExtra(EXTRA_KOLAJ); 26 | if (user instanceof User && collage instanceof Collage) { 27 | return CollageBuilderFragment.getInstance((User) user, (Collage) collage); 28 | } 29 | return null; 30 | } 31 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/CollagePreviewActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | import com.azoft.azoft.collage.data.CollageFillData; 6 | import com.azoft.azoft.collage.ui.fragments.CollagePreviewFragment; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * Date: 4/9/2014 12 | * Time: 5:56 PM 13 | * 14 | * @author MiG35 15 | */ 16 | public class CollagePreviewActivity extends SingleFragmentActivity { 17 | 18 | public static final String EXTRA_KOLAJ_FILL_DATA = 19 | "com.redmadrobot.azoft.collage.ui.activities.phone.CollagePreviewActivity.EXTRA_KOLAJ_FILL_DATA"; 20 | 21 | @Override 22 | protected Fragment createFragment() { 23 | final Serializable collageFillData = getIntent().getSerializableExtra(EXTRA_KOLAJ_FILL_DATA); 24 | if (collageFillData instanceof CollageFillData) { 25 | return CollagePreviewFragment.getInstance((CollageFillData) collageFillData); 26 | } 27 | return null; 28 | } 29 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/ImageSelectionFromUserFeedActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.app.Fragment; 5 | 6 | import com.azoft.azoft.collage.data.Post; 7 | import com.azoft.azoft.collage.data.User; 8 | import com.azoft.azoft.collage.ui.fragments.ImageSelectionFromUserFeedFragment; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * Date: 4/8/2014 14 | * Time: 7:03 PM 15 | * 16 | * @author MiG35 17 | */ 18 | public class ImageSelectionFromUserFeedActivity extends SingleFragmentActivity implements ImageSelectionFromUserFeedFragment.PostSelectionListener { 19 | 20 | public static final String RESULT_POST = "com.redmadrobot.azoft.collage.ui.fragments.ImageSelectionFromUserFeedFragment.RESULT_POST"; 21 | 22 | public static final String EXTRA_USER = "com.redmadrobot.azoft.collage.ui.activities.phone.ImageSelectionFromUserFeedActivity.EXTRA_USER"; 23 | 24 | @Override 25 | protected Fragment createFragment() { 26 | final Serializable user = getIntent().getSerializableExtra(EXTRA_USER); 27 | if (user instanceof User) { 28 | return ImageSelectionFromUserFeedFragment.getInstance((User) user); 29 | } 30 | return null; 31 | } 32 | 33 | @Override 34 | public void onPostSelected(final Post post) { 35 | final Intent intent = new Intent(); 36 | intent.putExtra(RESULT_POST, post); 37 | setResult(RESULT_OK, intent); 38 | finish(); 39 | } 40 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/InstagramAuthActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.graphics.Bitmap; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.webkit.CookieManager; 8 | import android.webkit.CookieSyncManager; 9 | import android.webkit.WebSettings; 10 | import android.webkit.WebView; 11 | import android.webkit.WebViewClient; 12 | 13 | import com.azoft.azoft.collage.R; 14 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 15 | import com.mig35.injectorlib.utils.inject.InjectView; 16 | 17 | public class InstagramAuthActivity extends CollageActivity { 18 | 19 | private static final String REDIRECT_URL = "http://www.azoft.com/"; 20 | private static final String CLIENT_ID = "2679711ad9b14dd3afa52c5c35acb5cd"; 21 | private static final String ACCESS_TOKEN_URL_PATH = "#access_token="; 22 | 23 | private static final String START_URL = "https://api.instagram.com/oauth/authorize/?client_id=%1$s&redirect_uri=%2$s&response_type=token&scope=public_content"; 24 | 25 | @InjectView(R.id.web_view) 26 | private WebView mWebView; 27 | @InjectView(R.id.progress_bar) 28 | private View mProgressView; 29 | 30 | @InjectSavedState 31 | private Bundle mWebViewState; 32 | 33 | @Override 34 | protected void onCreate(final Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | 37 | setContentView(R.layout.activity_instagram_auth); 38 | 39 | initWebView(null == savedInstanceState); 40 | 41 | mWebView.setWebViewClient(new AuthWebClient()); 42 | 43 | if (null == mWebViewState) { 44 | mWebView.loadUrl(String.format(START_URL, CLIENT_ID, REDIRECT_URL)); 45 | } else { 46 | mWebView.restoreState(mWebViewState); 47 | } 48 | } 49 | 50 | private void onTokenGranted(final String accessToken) { 51 | startActivity(CollageAuthActivity.createIntent(this, StartActivity.class, accessToken)); 52 | } 53 | 54 | private void onTokenFailed() { 55 | showToast(R.string.error_no_access_token_instagram); 56 | initWebView(true); 57 | mWebView.loadUrl(String.format(START_URL, CLIENT_ID, REDIRECT_URL)); 58 | } 59 | 60 | private void initWebView(final boolean clearSetup) { 61 | final WebSettings settings = mWebView.getSettings(); 62 | 63 | settings.setJavaScriptEnabled(true); 64 | settings.setDomStorageEnabled(true); 65 | settings.setAppCacheEnabled(true); 66 | settings.setSaveFormData(false); 67 | 68 | if (clearSetup) { 69 | mWebView.clearCache(true); 70 | mWebView.clearHistory(); 71 | clearCookies(); 72 | } 73 | } 74 | 75 | @Override 76 | protected void onSaveInstanceState(final Bundle outState) { 77 | mWebViewState = new Bundle(); 78 | mWebView.saveState(mWebViewState); 79 | super.onSaveInstanceState(outState); 80 | } 81 | 82 | @Override 83 | public void onBackPressed() { 84 | if (mWebView.canGoBack()) { 85 | mWebView.goBack(); 86 | } else { 87 | super.onBackPressed(); 88 | } 89 | } 90 | 91 | @SuppressWarnings("deprecation") 92 | public void clearCookies() { 93 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { 94 | CookieManager.getInstance().removeAllCookies(null); 95 | CookieManager.getInstance().flush(); 96 | } else { 97 | final CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this); 98 | cookieSyncManager.startSync(); 99 | final CookieManager cookieManager = CookieManager.getInstance(); 100 | cookieManager.removeAllCookie(); 101 | cookieManager.removeSessionCookie(); 102 | cookieSyncManager.stopSync(); 103 | cookieSyncManager.sync(); 104 | } 105 | } 106 | 107 | private class AuthWebClient extends WebViewClient { 108 | 109 | @Override 110 | public void onPageStarted(final WebView view, final String url, final Bitmap favicon) { 111 | super.onPageStarted(view, url, favicon); 112 | mProgressView.setVisibility(View.VISIBLE); 113 | } 114 | 115 | @Override 116 | public boolean shouldOverrideUrlLoading(final WebView view, final String url) { 117 | if (url.startsWith(REDIRECT_URL)) { 118 | final int accessTokenIndex = url.indexOf(ACCESS_TOKEN_URL_PATH); 119 | if (0 < accessTokenIndex) { 120 | final String accessToken = url.substring(accessTokenIndex + ACCESS_TOKEN_URL_PATH.length()); 121 | onTokenGranted(accessToken); 122 | } else { 123 | onTokenFailed(); 124 | } 125 | 126 | return true; 127 | } else { 128 | return false; 129 | } 130 | } 131 | 132 | @Override 133 | public void onPageFinished(final WebView view, final String url) { 134 | super.onPageFinished(view, url); 135 | mProgressView.setVisibility(View.GONE); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/SingleFragmentActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | 7 | import com.azoft.azoft.collage.R; 8 | 9 | /** 10 | * This activity will handle fragment creation and call createFragment when needed. 11 | *

12 | * Date: 4/8/2014 13 | * Time: 11:19 AM 14 | * 15 | * @author MiG35 16 | */ 17 | public abstract class SingleFragmentActivity extends CollageAuthActivity { 18 | 19 | @Override 20 | protected void onCreate(final Bundle savedInstanceState, final String accessToken) { 21 | setContentView(R.layout.activity_single_fragment); 22 | 23 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 24 | 25 | final FragmentManager fm = getSupportFragmentManager(); 26 | if (null == fm.findFragmentById(R.id.container)) { 27 | final Fragment fragment = createFragment(); 28 | if (null == fragment) { 29 | finish(); 30 | } else { 31 | fm.beginTransaction().add(R.id.container, fragment).commit(); 32 | } 33 | } 34 | } 35 | 36 | protected abstract Fragment createFragment(); 37 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/StartActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.FragmentManager; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | import android.widget.Button; 10 | 11 | import com.azoft.azoft.collage.R; 12 | import com.azoft.azoft.collage.data.Collage; 13 | import com.azoft.azoft.collage.data.User; 14 | import com.azoft.azoft.collage.ui.fragments.CollageSelectionFragment; 15 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 16 | import com.mig35.injectorlib.utils.inject.InjectView; 17 | 18 | /** 19 | * Date: 4/10/2014 20 | * Time: 11:57 AM 21 | * 22 | * @author MiG35 23 | */ 24 | public class StartActivity extends CollageAuthActivity { 25 | 26 | private static final int USER_SELECTION_REQUEST_CODE = 14; 27 | 28 | @InjectView(R.id.b_current_user) 29 | private Button mCurrentUserButton; 30 | 31 | @InjectSavedState 32 | private User mCurrentUser; 33 | 34 | @Override 35 | protected void onCreate(final Bundle savedInstanceState, final String accessToken) { 36 | setContentView(R.layout.activity_start); 37 | 38 | mCurrentUserButton.setOnClickListener(new View.OnClickListener() { 39 | @Override 40 | public void onClick(final View v) { 41 | startActivityForResult(CollageAuthActivity.createIntent(StartActivity.this, UserSelectionActivity.class), USER_SELECTION_REQUEST_CODE); 42 | } 43 | }); 44 | final FragmentManager fm = getSupportFragmentManager(); 45 | if (null == fm.findFragmentById(R.id.container_collage_selection)) { 46 | fm.beginTransaction().add(R.id.container_collage_selection, new CollageSelectionFragment()).commit(); 47 | } 48 | 49 | updateButtonText(); 50 | getSupportActionBar().setDisplayShowHomeEnabled(false); 51 | getSupportActionBar().setDisplayShowTitleEnabled(false); 52 | } 53 | 54 | @Override 55 | public boolean onCreateOptionsMenu(final Menu menu) { 56 | getMenuInflater().inflate(R.menu.start_collage, menu); 57 | 58 | super.onCreateOptionsMenu(menu); 59 | 60 | return true; 61 | } 62 | 63 | @Override 64 | public boolean onOptionsItemSelected(final MenuItem item) { 65 | if (item.getItemId() == R.id.menu_create_collage) { 66 | final CollageSelectionFragment collageSelectionFragment = 67 | (CollageSelectionFragment) getSupportFragmentManager().findFragmentById(R.id.container_collage_selection); 68 | final Collage collage = collageSelectionFragment.getSelectedCollage(); 69 | if (null == mCurrentUser) { 70 | showToast(R.string.error_select_user_first); 71 | } else if (null == collage) { 72 | showToast(R.string.error_select_collage_first); 73 | } else { 74 | openCollageBuilder(mCurrentUser, collage); 75 | } 76 | } 77 | return super.onOptionsItemSelected(item); 78 | } 79 | 80 | private void openCollageBuilder(final User currentUser, final Collage collage) { 81 | startActivity(CollageAuthActivity.createIntent(this, CollageBuilderActivity.class).putExtra(CollageBuilderActivity.EXTRA_USER, currentUser) 82 | .putExtra(CollageBuilderActivity.EXTRA_KOLAJ, collage)); 83 | } 84 | 85 | private void updateButtonText() { 86 | if (null == mCurrentUser) { 87 | mCurrentUserButton.setText(R.string.text_select_current_user); 88 | } else { 89 | mCurrentUserButton.setText(getString(R.string.text_current_user_selected, mCurrentUser.getNickName())); 90 | } 91 | } 92 | 93 | @Override 94 | protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { 95 | if (USER_SELECTION_REQUEST_CODE == requestCode && resultCode == RESULT_OK && null != data) { 96 | mCurrentUser = (User) data.getSerializableExtra(UserSelectionActivity.RESULT_USER); 97 | updateButtonText(); 98 | } else { 99 | super.onActivityResult(requestCode, resultCode, data); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/activities/phone/UserSelectionActivity.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.activities.phone; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.app.Fragment; 5 | 6 | import com.azoft.azoft.collage.data.User; 7 | import com.azoft.azoft.collage.ui.fragments.UserSelectionFragment; 8 | 9 | public class UserSelectionActivity extends SingleFragmentActivity implements UserSelectionFragment.OnUserSelectedListener { 10 | 11 | public static final String RESULT_USER = "com.redmadrobot.azoft.collage.ui.activities.phone.UserSelectionActivity.RESULT_USER"; 12 | 13 | @Override 14 | protected Fragment createFragment() { 15 | return new UserSelectionFragment(); 16 | } 17 | 18 | @Override 19 | public void onUserSelected(final User user) { 20 | setResult(RESULT_OK, new Intent().putExtra(RESULT_USER, user)); 21 | finish(); 22 | } 23 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/adapters/CollageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.ImageView; 9 | 10 | import com.azoft.azoft.collage.R; 11 | import com.azoft.azoft.collage.data.Collage; 12 | import com.azoft.azoft.collage.ui.widgets.CollageViewGroup; 13 | import com.azoft.azoft.collage.utils.collagegenerators.CollageFactory; 14 | 15 | /** 16 | *

17 | * Date: 4/8/2014 18 | * Time: 5:22 PM 19 | * 20 | * @author MiG35 21 | */ 22 | public class CollageAdapter extends BaseAdapter { 23 | 24 | private final CollageFactory mCollageFactory; 25 | private final LayoutInflater mLayoutInflater; 26 | 27 | private Integer mSelectedNumber; 28 | 29 | /** 30 | * Will show collage template items. Will use factory as collage generator. 31 | * 32 | * @param context application or activity context 33 | * @param collageFactory factory for collage creation 34 | * @param selectedNumber previously selected collage. may be null if nothing selected. 35 | */ 36 | public CollageAdapter(final Context context, final CollageFactory collageFactory, final Integer selectedNumber) { 37 | mLayoutInflater = LayoutInflater.from(context); 38 | mCollageFactory = collageFactory; 39 | mSelectedNumber = selectedNumber; 40 | } 41 | 42 | @Override 43 | public int getCount() { 44 | return mCollageFactory.getCollageCount(); 45 | } 46 | 47 | @Override 48 | public Collage getItem(final int position) { 49 | return mCollageFactory.getCollage(position); 50 | } 51 | 52 | @Override 53 | public long getItemId(final int position) { 54 | return position; 55 | } 56 | 57 | @Override 58 | public View getView(final int position, final View convertView, final ViewGroup parent) { 59 | final View resultView; 60 | final Holder holder; 61 | if (null == convertView) { 62 | resultView = mLayoutInflater.inflate(R.layout.item_collage, parent, false); 63 | holder = new Holder(resultView); 64 | //noinspection ConstantConditions 65 | resultView.setTag(R.id.holder, holder); 66 | } else { 67 | resultView = convertView; 68 | holder = (Holder) convertView.getTag(R.id.holder); 69 | } 70 | 71 | holder.mCollageViewGroup.setCollage(getItem(position)); 72 | if (null == mSelectedNumber || mSelectedNumber != position) { 73 | holder.mCheckBox.setVisibility(View.GONE); 74 | } else { 75 | holder.mCheckBox.setVisibility(View.VISIBLE); 76 | } 77 | 78 | return resultView; 79 | } 80 | 81 | /** 82 | * Will set new selected collage 83 | */ 84 | public void setSelected(final int position) { 85 | mSelectedNumber = position; 86 | notifyDataSetChanged(); 87 | } 88 | 89 | private static class Holder { 90 | 91 | final ImageView mCheckBox; 92 | final CollageViewGroup mCollageViewGroup; 93 | 94 | Holder(final View resultView) { 95 | mCheckBox = (ImageView) resultView.findViewById(R.id.iv_selection_box); 96 | mCollageViewGroup = (CollageViewGroup) resultView.findViewById(R.id.collage_view_group); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/adapters/UserFeedAdapter.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.ImageView; 9 | 10 | import com.azoft.azoft.collage.R; 11 | import com.azoft.azoft.collage.data.Post; 12 | import com.squareup.picasso.Callback; 13 | import com.squareup.picasso.Picasso; 14 | 15 | import java.lang.ref.WeakReference; 16 | import java.util.List; 17 | 18 | /** 19 | * Date: 4/9/2014 20 | * Time: 10:13 AM 21 | * 22 | * @author MiG35 23 | */ 24 | public class UserFeedAdapter extends BaseAdapter { 25 | 26 | private final LayoutInflater mLayoutInflater; 27 | private final List mPosts; 28 | 29 | public UserFeedAdapter(final Context context, final List posts) { 30 | mLayoutInflater = LayoutInflater.from(context); 31 | mPosts = posts; 32 | } 33 | 34 | @Override 35 | public int getCount() { 36 | return null == mPosts ? 0 : mPosts.size(); 37 | } 38 | 39 | @Override 40 | public Post getItem(final int position) { 41 | return mPosts.get(position); 42 | } 43 | 44 | @Override 45 | public long getItemId(final int position) { 46 | return position; 47 | } 48 | 49 | @Override 50 | public View getView(final int position, final View convertView, final ViewGroup parent) { 51 | final View resultView; 52 | final Holder holder; 53 | if (null == convertView) { 54 | resultView = mLayoutInflater.inflate(R.layout.item_user_feed, parent, false); 55 | holder = new Holder(resultView); 56 | //noinspection ConstantConditions 57 | resultView.setTag(R.id.holder, holder); 58 | } else { 59 | resultView = convertView; 60 | holder = (Holder) resultView.getTag(R.id.holder); 61 | } 62 | final Post post = getItem(position); 63 | 64 | holder.mProgressView.setVisibility(View.VISIBLE); 65 | 66 | Picasso.with(mLayoutInflater.getContext()).load(post.getLowResolutionImage().getUrl()) 67 | .resizeDimen(R.dimen.item_image_selection_grid_size, R.dimen.item_image_selection_grid_size).centerInside() 68 | .error(R.drawable.ic_action_picture).into(holder.mImageView, new ProgressCallback(holder.mProgressView)); 69 | 70 | return resultView; 71 | } 72 | 73 | private static class Holder { 74 | 75 | private final ImageView mImageView; 76 | private final View mProgressView; 77 | 78 | Holder(final View resultView) { 79 | mImageView = (ImageView) resultView.findViewById(R.id.iv_user_feed); 80 | mProgressView = resultView.findViewById(R.id.pb_user_feed); 81 | } 82 | } 83 | 84 | private static class ProgressCallback implements Callback { 85 | 86 | private final WeakReference mProgressViewRef; 87 | 88 | public ProgressCallback(final View progressView) { 89 | mProgressViewRef = new WeakReference<>(progressView); 90 | } 91 | 92 | @Override 93 | public void onSuccess() { 94 | doHide(); 95 | } 96 | 97 | @Override 98 | public void onError() { 99 | doHide(); 100 | } 101 | 102 | private void doHide() { 103 | final View view = mProgressViewRef.get(); 104 | if (null != view) { 105 | view.setVisibility(View.GONE); 106 | } 107 | mProgressViewRef.clear(); 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/adapters/UsersAdapter.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.adapters; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.azoft.azoft.collage.R; 13 | import com.azoft.azoft.collage.data.User; 14 | import com.squareup.picasso.Picasso; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Date: 4/8/2014 20 | * Time: 1:09 PM 21 | * 22 | * @author MiG35 23 | */ 24 | public class UsersAdapter extends BaseAdapter { 25 | 26 | private final LayoutInflater mLayoutInflater; 27 | private List mUsers; 28 | 29 | public UsersAdapter(final Context context, final List users) { 30 | mLayoutInflater = LayoutInflater.from(context); 31 | mUsers = users; 32 | } 33 | 34 | @Override 35 | public int getCount() { 36 | return null == mUsers ? 0 : mUsers.size(); 37 | } 38 | 39 | @Override 40 | public User getItem(final int position) { 41 | return mUsers.get(position); 42 | } 43 | 44 | @Override 45 | public long getItemId(final int position) { 46 | return position; 47 | } 48 | 49 | @Override 50 | public View getView(final int position, final View convertView, final ViewGroup parent) { 51 | final View resultView; 52 | final Holder holder; 53 | if (null == convertView) { 54 | resultView = mLayoutInflater.inflate(R.layout.item_user, parent, false); 55 | holder = new Holder(resultView); 56 | //noinspection ConstantConditions 57 | resultView.setTag(R.id.holder, holder); 58 | } else { 59 | resultView = convertView; 60 | holder = (Holder) resultView.getTag(R.id.holder); 61 | } 62 | final User user = getItem(position); 63 | 64 | final String userName = getUserName(user); 65 | holder.mNickNameTextView.setText(user.getNickName()); 66 | holder.mNameTextView.setText(userName); 67 | holder.mNameTextView.setVisibility(null == userName ? View.GONE : View.VISIBLE); 68 | 69 | Picasso.with(mLayoutInflater.getContext()).load(user.getProfilePicture()) 70 | .resizeDimen(R.dimen.item_user_picture_size, R.dimen.item_user_picture_size).centerInside().error(R.drawable.ic_action_user) 71 | .into(holder.mPictureImageView); 72 | 73 | return resultView; 74 | } 75 | 76 | private String getUserName(final User user) { 77 | final StringBuilder result = new StringBuilder(10); 78 | if (!TextUtils.isEmpty(user.getFirstName())) { 79 | result.append(user.getFirstName()); 80 | 81 | } 82 | if (!TextUtils.isEmpty(user.getLastName())) { 83 | if (result.length() != 0) { 84 | result.append(' '); 85 | } 86 | result.append(user.getLastName()); 87 | } 88 | if (result.length() == 0) { 89 | return null; 90 | } 91 | return result.toString(); 92 | } 93 | 94 | public void setUsers(final List users) { 95 | mUsers = users; 96 | notifyDataSetChanged(); 97 | } 98 | 99 | private static class Holder { 100 | 101 | 102 | final ImageView mPictureImageView; 103 | final TextView mNickNameTextView; 104 | final TextView mNameTextView; 105 | 106 | Holder(final View resultView) { 107 | mPictureImageView = (ImageView) resultView.findViewById(R.id.iv_picture); 108 | mNickNameTextView = (TextView) resultView.findViewById(R.id.tv_nickname); 109 | mNameTextView = (TextView) resultView.findViewById(R.id.tv_name); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/fragments/ActionBarLoaderFragment.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.fragments; 2 | 3 | import android.app.Activity; 4 | 5 | import com.azoft.azoft.collage.ui.activities.phone.CollageAuthActivity; 6 | import com.mig35.loaderlib.ui.LoaderFragment; 7 | import com.azoft.azoft.collage.ui.activities.phone.CollageActivity; 8 | 9 | /** 10 | * Base fragment. Can be used only with CollageActivity. 11 | *

12 | * Date: 4/8/2014 13 | * Time: 4:51 PM 14 | * 15 | * @author MiG35 16 | */ 17 | public abstract class ActionBarLoaderFragment extends LoaderFragment { 18 | 19 | @Override 20 | public void onAttach(final Activity activity) { 21 | super.onAttach(activity); 22 | 23 | if (!(activity instanceof CollageAuthActivity)) { 24 | throw new IllegalStateException("This fragment should be used only with CollageActivity"); 25 | } 26 | } 27 | 28 | public CollageAuthActivity getActionBarActivity() { 29 | return (CollageAuthActivity) getActivity(); 30 | } 31 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/fragments/CollageBuilderFragment.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.fragments; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.Menu; 10 | import android.view.MenuInflater; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import com.azoft.azoft.collage.R; 16 | import com.azoft.azoft.collage.data.Collage; 17 | import com.azoft.azoft.collage.data.CollageFillData; 18 | import com.azoft.azoft.collage.data.CollageRegionData; 19 | import com.azoft.azoft.collage.data.Post; 20 | import com.azoft.azoft.collage.data.User; 21 | import com.azoft.azoft.collage.loaders.CollageBigImageLoader; 22 | import com.azoft.azoft.collage.ui.activities.phone.CollageAuthActivity; 23 | import com.azoft.azoft.collage.ui.activities.phone.CollagePreviewActivity; 24 | import com.azoft.azoft.collage.ui.activities.phone.ImageSelectionFromUserFeedActivity; 25 | import com.azoft.azoft.collage.ui.widgets.CollageViewGroup; 26 | import com.azoft.azoft.collage.utils.CollageRegion; 27 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 28 | import com.mig35.injectorlib.utils.inject.InjectView; 29 | 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | 33 | /** 34 | * Will show collage and performs all actions for its filling. 35 | *

36 | * Date: 4/8/2014 37 | * Time: 6:41 PM 38 | * 39 | * @author MiG35 40 | */ 41 | public class CollageBuilderFragment extends ActionBarLoaderFragment { 42 | 43 | private static final String BIG_IMAGE_LOADER = "com.redmadrobot.azoft.collage.ui.fragments.CollageBuilderFragment.BIG_IMAGE_LOADER_%d"; 44 | private static final int REQUEST_CODE_IMAGE_SELECTION = 156; 45 | 46 | @InjectSavedState 47 | private User mUser; 48 | @InjectSavedState 49 | private CollageFillData mCollageFillData; 50 | @InjectSavedState 51 | private CollageRegion mSelectedCollageRegion; 52 | 53 | @InjectView(R.id.collage_view_group) 54 | private CollageViewGroup mCollageViewGroup; 55 | 56 | // this is waiting images. user ask as to load them to collage regions, but we may not load them because of full recreation (as if don't keep 57 | // activities set). 58 | @SuppressWarnings("FieldMayBeFinal") 59 | @InjectSavedState 60 | private Map mWaitingImages = new HashMap<>(); 61 | 62 | public CollageBuilderFragment() { 63 | setHasOptionsMenu(true); 64 | setMenuVisibility(true); 65 | } 66 | 67 | @Override 68 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 69 | return inflater.inflate(R.layout.fragment_collage_builder, container, false); 70 | } 71 | 72 | @Override 73 | public void onViewCreated(final View view, final Bundle savedInstanceState) { 74 | super.onViewCreated(view, savedInstanceState); 75 | 76 | mCollageViewGroup.setCollage(mCollageFillData); 77 | mCollageViewGroup.setRegionClickListener(new CollageViewGroup.RegionClickListener() { 78 | @Override 79 | public void onRegionClicked(final CollageRegion collageRegion) { 80 | mSelectedCollageRegion = collageRegion; 81 | startActivityForResult(CollageAuthActivity.createIntent(getActionBarActivity(), ImageSelectionFromUserFeedActivity.class) 82 | .putExtra(ImageSelectionFromUserFeedActivity.EXTRA_USER, mUser), REQUEST_CODE_IMAGE_SELECTION); 83 | } 84 | }); 85 | 86 | for (final Map.Entry setItem : mWaitingImages.entrySet()) { 87 | // check if we have waiting images and they are not loaded yet 88 | if (!getLoaderHelper().hasLoader(getLoaderHelper().getLoaderId(createCollageRegionLoaderId(setItem.getKey())))) { 89 | // we should start this loader again 90 | //noinspection ObjectAllocationInLoop 91 | getLoaderHelper().initAsyncLoader(getLoaderHelper().getLoaderId(createCollageRegionLoaderId(setItem.getKey())), 92 | new CollageBigImageLoader(getActivity(), setItem.getKey(), setItem.getValue())); 93 | } 94 | } 95 | } 96 | 97 | @Override 98 | public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 99 | super.onCreateOptionsMenu(menu, inflater); 100 | 101 | inflater.inflate(R.menu.collage_builder, menu); 102 | } 103 | 104 | @Override 105 | public boolean onOptionsItemSelected(final MenuItem item) { 106 | if (R.id.menu_preview_collage == item.getItemId()) { 107 | if (mCollageFillData.hasAllRegions()) { 108 | startActivity(CollageAuthActivity.createIntent(getActionBarActivity(), CollagePreviewActivity.class) 109 | .putExtra(CollagePreviewActivity.EXTRA_KOLAJ_FILL_DATA, mCollageFillData)); 110 | } else { 111 | getActionBarActivity().showToast(R.string.error_fill_all_items_first); 112 | } 113 | return true; 114 | } 115 | return super.onOptionsItemSelected(item); 116 | } 117 | 118 | @Override 119 | public void onLoaderResult(final int id, final Object result) { 120 | super.onLoaderResult(id, result); 121 | 122 | for (final CollageRegion collageRegion : mCollageFillData.getCollageRegions()) { 123 | if (id == getLoaderHelper().getLoaderId(createCollageRegionLoaderId(collageRegion))) { 124 | getLoaderHelper().removeLoaderFromRunningLoaders(id); 125 | 126 | final CollageRegionData collageRegionData = (CollageRegionData) result; 127 | mCollageFillData.setRegionData(collageRegion, collageRegionData); 128 | mCollageViewGroup.invalidateRegionData(); 129 | 130 | // now we finished our loading, so it will be saved in save state, so it is save to remove it from this list. 131 | mWaitingImages.remove(collageRegion); 132 | 133 | break; 134 | } 135 | } 136 | } 137 | 138 | @Override 139 | public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { 140 | if (REQUEST_CODE_IMAGE_SELECTION == requestCode) { 141 | if (resultCode == Activity.RESULT_OK && null != data) { 142 | if (null == mSelectedCollageRegion) { 143 | Log.e(CollageBuilderFragment.class.getSimpleName(), "Wrong state! How does it happen?! =/"); 144 | return; 145 | } 146 | final Post post = (Post) data.getSerializableExtra(ImageSelectionFromUserFeedActivity.RESULT_POST); 147 | // we should remember that this image is not loading 148 | mWaitingImages.put(mSelectedCollageRegion, post); 149 | getLoaderHelper().restartAsyncLoader(getLoaderHelper().getLoaderId(createCollageRegionLoaderId(mSelectedCollageRegion)), 150 | new CollageBigImageLoader(getActivity(), mSelectedCollageRegion, post)); 151 | } 152 | mSelectedCollageRegion = null; 153 | } else { 154 | super.onActivityResult(requestCode, resultCode, data); 155 | } 156 | } 157 | 158 | private String createCollageRegionLoaderId(final CollageRegion collageRegion) { 159 | return String.format(BIG_IMAGE_LOADER, collageRegion.getId()); 160 | } 161 | 162 | public static Fragment getInstance(final User user, final Collage collage) { 163 | if (null == user || null == collage) { 164 | throw new IllegalArgumentException("non user neither collage can be null"); 165 | } 166 | final CollageBuilderFragment collageBuilderFragment = new CollageBuilderFragment(); 167 | collageBuilderFragment.mUser = user; 168 | collageBuilderFragment.mCollageFillData = new CollageFillData(collage); 169 | return collageBuilderFragment; 170 | } 171 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/fragments/CollagePreviewFragment.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.fragments; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.content.DialogInterface; 6 | import android.os.Bundle; 7 | import android.support.annotation.NonNull; 8 | import android.support.v4.app.DialogFragment; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.app.FragmentManager; 11 | import android.view.LayoutInflater; 12 | import android.view.Menu; 13 | import android.view.MenuInflater; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.ImageView; 18 | 19 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 20 | import com.mig35.injectorlib.utils.inject.InjectView; 21 | import com.azoft.azoft.collage.R; 22 | import com.azoft.azoft.collage.data.CollageFillData; 23 | import com.azoft.azoft.collage.exceptions.CollageCreationException; 24 | import com.azoft.azoft.collage.loaders.CollagePreviewCreatorLoader; 25 | import com.azoft.azoft.collage.utils.CommonUtils; 26 | import com.squareup.picasso.Picasso; 27 | 28 | /** 29 | * Date: 4/9/2014 30 | * Time: 5:57 PM 31 | * 32 | * @author MiG35 33 | */ 34 | public class CollagePreviewFragment extends ActionBarLoaderFragment { 35 | 36 | private static final String KOLAJ_PREVIEW_LOADER = "com.redmadrobot.azoft.collage.ui.fragments.CollagePreviewFragment.KOLAJ_PREVIEW_LOADER"; 37 | private static final String CREATION_PROBLEM_FRAGMENT = 38 | "com.redmadrobot.azoft.collage.ui.fragments.CollagePreviewFragment.CREATION_PROBLEM_FRAGMENT"; 39 | 40 | @InjectSavedState 41 | private CollageFillData mCollageFillData; 42 | 43 | @InjectView(R.id.iv_collage) 44 | private ImageView mCollageImageView; 45 | @InjectView(R.id.progress_bar) 46 | private View mProgressView; 47 | 48 | @InjectSavedState 49 | private String mResultPath; 50 | 51 | public CollagePreviewFragment() { 52 | setHasOptionsMenu(true); 53 | setMenuVisibility(true); 54 | } 55 | 56 | @Override 57 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 58 | return inflater.inflate(R.layout.fragment_collage_preview, container, false); 59 | } 60 | 61 | @Override 62 | public void onViewCreated(final View view, final Bundle savedInstanceState) { 63 | super.onViewCreated(view, savedInstanceState); 64 | 65 | // we should check if there is no big errors in previous image creation 66 | if (null == getFragmentManager().findFragmentByTag(CREATION_PROBLEM_FRAGMENT)) { 67 | // we should check if we already create image or not 68 | if (null == mResultPath) { 69 | mProgressView.setVisibility(View.VISIBLE); 70 | getLoaderHelper().initAsyncLoader(getLoaderHelper().getLoaderId(KOLAJ_PREVIEW_LOADER), 71 | new CollagePreviewCreatorLoader(getActivity(), mCollageFillData)); 72 | } else { 73 | setResultPath(mResultPath); 74 | } 75 | } 76 | } 77 | 78 | @Override 79 | public void onLoaderResult(final int id, final Object result) { 80 | super.onLoaderResult(id, result); 81 | 82 | if (id == getLoaderHelper().getLoaderId(KOLAJ_PREVIEW_LOADER)) { 83 | getLoaderHelper().removeLoaderFromRunningLoaders(id); 84 | 85 | setResultPath((String) result); 86 | } 87 | } 88 | 89 | @Override 90 | public void onLoaderError(final int id, final Exception exception) { 91 | super.onLoaderError(id, exception); 92 | 93 | if (id == getLoaderHelper().getLoaderId(KOLAJ_PREVIEW_LOADER) && exception instanceof CollageCreationException) { 94 | final FragmentManager fm = getFragmentManager(); 95 | fm.beginTransaction().add(new CollageCreationProblemMessageFragment(), CREATION_PROBLEM_FRAGMENT).commitAllowingStateLoss(); 96 | } 97 | } 98 | 99 | private void setResultPath(final String resultPath) { 100 | mResultPath = resultPath; 101 | mProgressView.setVisibility(View.GONE); 102 | 103 | Picasso.with(getActivity()).load(resultPath).fit().error(R.drawable.ic_action_new).skipMemoryCache().into(mCollageImageView); 104 | getActionBarActivity().supportInvalidateOptionsMenu(); 105 | } 106 | 107 | @Override 108 | public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 109 | super.onCreateOptionsMenu(menu, inflater); 110 | 111 | if (null != mResultPath) { 112 | inflater.inflate(R.menu.collage_preview, menu); 113 | } 114 | } 115 | 116 | @Override 117 | public boolean onOptionsItemSelected(final MenuItem item) { 118 | if (item.getItemId() == R.id.menu_share_collage) { 119 | if (null != mResultPath) { 120 | // because we use internal storage and provider, we should grand other apps permissions and so on... 121 | CommonUtils.sendEmailWithAttachment(getActivity(), mResultPath, getString(R.string.text_email_subject)); 122 | } 123 | return true; 124 | } 125 | return super.onOptionsItemSelected(item); 126 | } 127 | 128 | public static Fragment getInstance(final CollageFillData collageFillData) { 129 | if (null == collageFillData) { 130 | throw new IllegalArgumentException("collageFillData can't be null"); 131 | } 132 | final CollagePreviewFragment collagePreviewFragment = new CollagePreviewFragment(); 133 | collagePreviewFragment.mCollageFillData = collageFillData; 134 | return collagePreviewFragment; 135 | } 136 | 137 | public static class CollageCreationProblemMessageFragment extends DialogFragment { 138 | 139 | @NonNull 140 | @Override 141 | public Dialog onCreateDialog(final Bundle savedInstanceState) { 142 | final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 143 | builder.setTitle(R.string.dialog_collage_creation_problem_title); 144 | builder.setMessage(R.string.dialog_collage_creation_problem_message); 145 | builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { 146 | @Override 147 | public void onClick(final DialogInterface dialog, final int which) { 148 | getActivity().finish(); 149 | } 150 | }); 151 | 152 | return builder.create(); 153 | } 154 | 155 | @Override 156 | public void onCancel(final DialogInterface dialog) { 157 | super.onCancel(dialog); 158 | 159 | getActivity().finish(); 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/fragments/CollageSelectionFragment.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.AdapterView; 8 | import android.widget.GridView; 9 | 10 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 11 | import com.mig35.injectorlib.utils.inject.InjectView; 12 | import com.azoft.azoft.collage.R; 13 | import com.azoft.azoft.collage.data.Collage; 14 | import com.azoft.azoft.collage.ui.adapters.CollageAdapter; 15 | import com.azoft.azoft.collage.utils.collagegenerators.CollageFactory; 16 | import com.azoft.azoft.collage.utils.collagegenerators.SimpleCollageGenerator; 17 | 18 | /** 19 | * Date: 4/8/2014 20 | * Time: 4:05 PM 21 | * 22 | * @author MiG35 23 | */ 24 | public class CollageSelectionFragment extends ActionBarLoaderFragment { 25 | 26 | private static final CollageFactory COLLAGE_FACTORY = new SimpleCollageGenerator(); 27 | 28 | @InjectSavedState 29 | private Integer mSelectedCollage; 30 | 31 | @InjectView(R.id.gv_collagees) 32 | private GridView mCollageGridView; 33 | 34 | @Override 35 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 36 | return inflater.inflate(R.layout.fragment_collage_selection, container, false); 37 | } 38 | 39 | @Override 40 | public void onViewCreated(final View view, final Bundle savedInstanceState) { 41 | super.onViewCreated(view, savedInstanceState); 42 | 43 | final CollageAdapter collageAdapter = new CollageAdapter(getActivity(), COLLAGE_FACTORY, mSelectedCollage); 44 | mCollageGridView.setAdapter(collageAdapter); 45 | mCollageGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 46 | @Override 47 | public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { 48 | collageAdapter.setSelected(position); 49 | mSelectedCollage = position; 50 | } 51 | }); 52 | } 53 | 54 | public Collage getSelectedCollage() { 55 | if (null == mSelectedCollage) { 56 | return null; 57 | } 58 | return COLLAGE_FACTORY.getCollage(mSelectedCollage); 59 | } 60 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/fragments/ImageSelectionFromUserFeedFragment.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.fragments; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.view.Menu; 9 | import android.view.MenuInflater; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.AdapterView; 14 | import android.widget.GridView; 15 | import android.widget.TextView; 16 | 17 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 18 | import com.mig35.injectorlib.utils.inject.InjectView; 19 | import com.azoft.azoft.collage.R; 20 | import com.azoft.azoft.collage.data.Post; 21 | import com.azoft.azoft.collage.data.User; 22 | import com.azoft.azoft.collage.data.results.UserRecentFeedResult; 23 | import com.azoft.azoft.collage.loaders.UserImageFeedLoader; 24 | import com.azoft.azoft.collage.ui.adapters.UserFeedAdapter; 25 | 26 | /** 27 | * Will load and show user's image feed. Will notify ParentFragment or Activity if post is clicked if they implement PostSelectionListener 28 | * interface. 29 | *

30 | * Date: 4/8/2014 31 | * Time: 7:13 PM 32 | * 33 | * @author MiG35 34 | */ 35 | public class ImageSelectionFromUserFeedFragment extends ActionBarLoaderFragment { 36 | 37 | private static final String USER_FEED_LOADER = "com.redmadrobot.azoft.collage.ui.fragments.ImageSelectionFromUserFeedFragment.USER_FEED_LOADER"; 38 | 39 | // we use aggressive cache method. see readme for details 40 | @SuppressWarnings("StaticNonFinalField") 41 | private static UserRecentFeedResult sUserRecentFeedResult; 42 | 43 | @InjectView(R.id.gv_image_selection) 44 | private GridView mImageSelectionGridView; 45 | @InjectView(R.id.tv_empty) 46 | private TextView mEmptyTextView; 47 | @InjectView(R.id.progress_bar) 48 | private View mProgressBar; 49 | 50 | @InjectSavedState 51 | private User mUser; 52 | 53 | public ImageSelectionFromUserFeedFragment() { 54 | setHasOptionsMenu(true); 55 | setMenuVisibility(true); 56 | } 57 | 58 | @Override 59 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 60 | return inflater.inflate(R.layout.fragment_image_selection_from_user_feed, container, false); 61 | } 62 | 63 | @Override 64 | public void onViewCreated(final View view, final Bundle savedInstanceState) { 65 | super.onViewCreated(view, savedInstanceState); 66 | 67 | mImageSelectionGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 68 | @Override 69 | public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { 70 | final Object postObj = parent.getItemAtPosition(position); 71 | if (postObj instanceof Post) { 72 | final Post post = (Post) postObj; 73 | // we should check our parent fragment and then activity if they are listening for post selection 74 | final Fragment parentFragment = getParentFragment(); 75 | final Activity activity = getActivity(); 76 | if (parentFragment instanceof PostSelectionListener) { 77 | ((PostSelectionListener) parentFragment).onPostSelected(post); 78 | } else if (activity instanceof PostSelectionListener) { 79 | ((PostSelectionListener) activity).onPostSelected(post); 80 | } else { 81 | Log.e(ImageSelectionFromUserFeedFragment.class.getSimpleName(), "post selected, but no listener found"); 82 | } 83 | } 84 | } 85 | }); 86 | // we use aggressive user feed cache in static variable 87 | if (null != sUserRecentFeedResult && mUser.getId().equals(sUserRecentFeedResult.getUserId())) { 88 | setUserRecentFeedResult(sUserRecentFeedResult); 89 | } else { 90 | State.PROGRESS.apply(this); 91 | getLoaderHelper().initAsyncLoader(getLoaderHelper().getLoaderId(USER_FEED_LOADER), new UserImageFeedLoader(getActivity(), mUser.getId(), getActionBarActivity().getAccessToken())); 92 | } 93 | } 94 | 95 | @Override 96 | public void onLoaderResult(final int id, final Object result) { 97 | super.onLoaderResult(id, result); 98 | 99 | if (id == getLoaderHelper().getLoaderId(USER_FEED_LOADER)) { 100 | getLoaderHelper().removeLoaderFromRunningLoaders(id); 101 | 102 | setUserRecentFeedResult((UserRecentFeedResult) result); 103 | 104 | getActionBarActivity().supportInvalidateOptionsMenu(); 105 | } 106 | } 107 | 108 | @Override 109 | public void onLoaderError(final int id, final Exception exception) { 110 | super.onLoaderError(id, exception); 111 | 112 | getLoaderHelper().destroyAsyncLoader(id); 113 | getActionBarActivity().supportInvalidateOptionsMenu(); 114 | } 115 | 116 | private void setUserRecentFeedResult(final UserRecentFeedResult userRecentFeedResult) { 117 | // we use aggressive cache method. see readme for details 118 | //noinspection AssignmentToStaticFieldFromInstanceMethod 119 | sUserRecentFeedResult = userRecentFeedResult; 120 | 121 | final UserFeedAdapter adapter = new UserFeedAdapter(getActivity(), userRecentFeedResult.getPosts()); 122 | 123 | if (0 == adapter.getCount()) { 124 | State.EMPTY.apply(this); 125 | } else { 126 | mImageSelectionGridView.setAdapter(adapter); 127 | State.DATA.apply(this); 128 | } 129 | } 130 | 131 | @Override 132 | public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 133 | super.onCreateOptionsMenu(menu, inflater); 134 | 135 | if (!getLoaderHelper().hasRunningLoaders()) { 136 | inflater.inflate(R.menu.image_selection_user_feed, menu); 137 | } 138 | } 139 | 140 | @Override 141 | public boolean onOptionsItemSelected(final MenuItem item) { 142 | if (item.getItemId() == R.id.menu_update) { 143 | getLoaderHelper() 144 | .restartAsyncLoader(getLoaderHelper().getLoaderId(USER_FEED_LOADER), new UserImageFeedLoader(getActivity(), mUser.getId(), getActionBarActivity().getAccessToken())); 145 | getActionBarActivity().supportInvalidateOptionsMenu(); 146 | return true; 147 | } 148 | return super.onOptionsItemSelected(item); 149 | } 150 | 151 | public static Fragment getInstance(final User user) { 152 | if (null == user) { 153 | throw new IllegalArgumentException("user can't be null"); 154 | } 155 | final ImageSelectionFromUserFeedFragment imageSelectionFromUserFeedFragment = new ImageSelectionFromUserFeedFragment(); 156 | imageSelectionFromUserFeedFragment.mUser = user; 157 | return imageSelectionFromUserFeedFragment; 158 | } 159 | 160 | public interface PostSelectionListener { 161 | 162 | void onPostSelected(final Post post); 163 | } 164 | 165 | private enum State { 166 | DATA { 167 | @Override 168 | public void apply(final ImageSelectionFromUserFeedFragment fragment) { 169 | fragment.mImageSelectionGridView.setVisibility(View.VISIBLE); 170 | fragment.mEmptyTextView.setVisibility(View.GONE); 171 | fragment.mProgressBar.setVisibility(View.GONE); 172 | } 173 | }, 174 | PROGRESS { 175 | @Override 176 | public void apply(final ImageSelectionFromUserFeedFragment fragment) { 177 | fragment.mImageSelectionGridView.setVisibility(View.GONE); 178 | fragment.mEmptyTextView.setVisibility(View.GONE); 179 | fragment.mProgressBar.setVisibility(View.VISIBLE); 180 | } 181 | }, 182 | EMPTY { 183 | @Override 184 | public void apply(final ImageSelectionFromUserFeedFragment fragment) { 185 | fragment.mImageSelectionGridView.setVisibility(View.GONE); 186 | fragment.mEmptyTextView.setVisibility(View.VISIBLE); 187 | fragment.mProgressBar.setVisibility(View.GONE); 188 | fragment.mEmptyTextView.setText(fragment.getString(R.string.text_user_no_images, fragment.mUser.getNickName())); 189 | } 190 | }; 191 | 192 | public abstract void apply(final ImageSelectionFromUserFeedFragment fragment); 193 | } 194 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/fragments/UserSelectionFragment.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.fragments; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.view.MenuItemCompat; 8 | import android.support.v7.widget.SearchView; 9 | import android.util.Log; 10 | import android.view.Gravity; 11 | import android.view.LayoutInflater; 12 | import android.view.Menu; 13 | import android.view.MenuInflater; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.view.inputmethod.InputMethodManager; 18 | import android.widget.AdapterView; 19 | import android.widget.ListView; 20 | import android.widget.TextView; 21 | 22 | import com.mig35.injectorlib.utils.inject.InjectSavedState; 23 | import com.mig35.injectorlib.utils.inject.InjectView; 24 | import com.azoft.azoft.collage.R; 25 | import com.azoft.azoft.collage.data.User; 26 | import com.azoft.azoft.collage.data.results.UserDataResult; 27 | import com.azoft.azoft.collage.data.results.UsersSearchResult; 28 | import com.azoft.azoft.collage.loaders.UserDataLoader; 29 | import com.azoft.azoft.collage.loaders.UsersSearchLoader; 30 | import com.azoft.azoft.collage.ui.adapters.UsersAdapter; 31 | 32 | import java.util.regex.Pattern; 33 | 34 | /** 35 | * Will load and show users for selected nickname. Will notify ParentFragment or Activity if user is clicked if they implement OnUserSelectedListener 36 | * interface. 37 | * Will use user's name pattern as Instagram has. 38 | * Date: 4/8/2014 39 | * Time: 4:49 PM 40 | * 41 | * @author MiG35 42 | */ 43 | public class UserSelectionFragment extends ActionBarLoaderFragment { 44 | 45 | private static final String LOADER_USER_HAS_DATA = "com.redmadrobot.azoft.collage.ui.activities.phone.UserSelectionActivity.LOADER_USER_HAS_DATA"; 46 | private static final String LOADER_USERS = "com.redmadrobot.azoft.collage.ui.activities.phone.UserSelectionActivity.LOADER_USERS"; 47 | 48 | private static final Pattern USER_NAME_PATTERN = Pattern.compile("[\\w]+"); 49 | 50 | @InjectView(R.id.lv_user_accounts) 51 | private ListView mUserAccountsListView; 52 | @InjectView(R.id.tv_empty) 53 | private TextView mEmptyTextView; 54 | 55 | @InjectSavedState 56 | private boolean mExpandMenu = true; 57 | @InjectSavedState 58 | private String mSearchString; 59 | 60 | @InjectSavedState 61 | private UsersSearchResult mUsersSearchResult; 62 | 63 | private UsersAdapter mUsersAdapter; 64 | 65 | public UserSelectionFragment() { 66 | setHasOptionsMenu(true); 67 | setMenuVisibility(true); 68 | } 69 | 70 | @Override 71 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 72 | return inflater.inflate(R.layout.fragment_user_selection, container, false); 73 | } 74 | 75 | @Override 76 | public void onViewCreated(final View view, final Bundle savedInstanceState) { 77 | super.onViewCreated(view, savedInstanceState); 78 | 79 | mUserAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 80 | 81 | private User mPrevSelectedUser; 82 | 83 | @Override 84 | public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { 85 | final User user = mUsersAdapter.getItem(position); 86 | if (mPrevSelectedUser == user) { 87 | getLoaderHelper().initAsyncLoader(getLoaderHelper().getLoaderId(LOADER_USER_HAS_DATA), new UserDataLoader(getActivity(), user, getActionBarActivity().getAccessToken())); 88 | } else { 89 | mPrevSelectedUser = user; 90 | getLoaderHelper() 91 | .restartAsyncLoader(getLoaderHelper().getLoaderId(LOADER_USER_HAS_DATA), new UserDataLoader(getActivity(), user, getActionBarActivity().getAccessToken())); 92 | } 93 | } 94 | }); 95 | 96 | if (getLoaderHelper().hasLoader(getLoaderHelper().getLoaderId(LOADER_USERS))) { 97 | getLoaderHelper().initAsyncLoader(getLoaderHelper().getLoaderId(LOADER_USERS), null); 98 | } else if (null != mSearchString) { 99 | onUserNickSelected(mSearchString); 100 | } else { 101 | getActionBarActivity().getSupportActionBar().setTitle(R.string.title_user_nick_selection); 102 | } 103 | } 104 | 105 | private void onUserNickSelected(final String nickName) { 106 | if (nickName.equals(mSearchString) && null != mUsersSearchResult) { 107 | // if we have all information for this input, simply show it 108 | setUserSearchResult(mUsersSearchResult); 109 | } else if (nickName.equals(mSearchString)) { 110 | // is we already enter it, but don't have data, we should connect to last loader 111 | getLoaderHelper().initAsyncLoader(getLoaderHelper().getLoaderId(LOADER_USERS), new UsersSearchLoader(getActivity(), nickName, getActionBarActivity().getAccessToken())); 112 | } else { 113 | // or we should load new data 114 | mUsersSearchResult = null; 115 | mSearchString = nickName; 116 | getLoaderHelper().restartAsyncLoader(getLoaderHelper().getLoaderId(LOADER_USERS), new UsersSearchLoader(getActivity(), nickName, getActionBarActivity().getAccessToken())); 117 | } 118 | } 119 | 120 | @Override 121 | public void onLoaderResult(final int id, final Object result) { 122 | super.onLoaderResult(id, result); 123 | 124 | if (id == getLoaderHelper().getLoaderId(LOADER_USERS)) { 125 | getLoaderHelper().removeLoaderFromRunningLoaders(id); 126 | 127 | setUserSearchResult((UsersSearchResult) result); 128 | } else if (id == getLoaderHelper().getLoaderId(LOADER_USER_HAS_DATA)) { 129 | getLoaderHelper().destroyAsyncLoader(id); 130 | 131 | final UserDataResult userDataResult = (UserDataResult) result; 132 | 133 | if (userDataResult.isHasImages()) { 134 | final User user = userDataResult.getUser(); 135 | final Fragment parentFragment = getParentFragment(); 136 | final Activity activity = getActivity(); 137 | if (parentFragment instanceof OnUserSelectedListener) { 138 | ((OnUserSelectedListener) parentFragment).onUserSelected(user); 139 | } else if (activity instanceof OnUserSelectedListener) { 140 | ((OnUserSelectedListener) activity).onUserSelected(user); 141 | } else { 142 | Log.e(UserSelectionFragment.class.getSimpleName(), "user selected, but no listener found"); 143 | } 144 | } else if (userDataResult.isDataViewAllowed()) { 145 | getActionBarActivity().showToast(R.string.error_user_no_images, Gravity.CENTER); 146 | } else { 147 | getActionBarActivity().showToast(R.string.error_not_allowed, Gravity.CENTER); 148 | } 149 | } 150 | } 151 | 152 | private void setUserSearchResult(final UsersSearchResult userSearchResult) { 153 | mUsersSearchResult = userSearchResult; 154 | getActionBarActivity().getSupportActionBar().setTitle(getString(R.string.title_user_nick_selection_result, userSearchResult.getNickName())); 155 | 156 | if (null == mUsersAdapter) { 157 | mUsersAdapter = new UsersAdapter(getActivity(), userSearchResult.getUsers()); 158 | } else { 159 | mUsersAdapter.setUsers(userSearchResult.getUsers()); 160 | } 161 | if (null == mUserAccountsListView.getAdapter()) { 162 | mUserAccountsListView.setAdapter(mUsersAdapter); 163 | } 164 | mUserAccountsListView.setVisibility(mUsersAdapter.getCount() == 0 ? View.GONE : View.VISIBLE); 165 | mEmptyTextView.setVisibility(mUsersAdapter.getCount() == 0 ? View.VISIBLE : View.GONE); 166 | mEmptyTextView.setText(getString(R.string.text_no_users_found_for, userSearchResult.getNickName())); 167 | } 168 | 169 | @Override 170 | public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 171 | super.onCreateOptionsMenu(menu, inflater); 172 | 173 | inflater.inflate(R.menu.user_account_selection, menu); 174 | 175 | initSearchView(menu.findItem(R.id.menu_search)); 176 | } 177 | 178 | @SuppressWarnings("ConstantConditions") 179 | private void initSearchView(final MenuItem searchMenuItem) { 180 | final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem); 181 | searchView.setQueryHint(getString(R.string.hint_enter_user_name)); 182 | searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 183 | 184 | @Override 185 | public boolean onQueryTextSubmit(final String s) { 186 | final String nickName = s.trim(); 187 | if (USER_NAME_PATTERN.matcher(nickName).matches()) { 188 | MenuItemCompat.collapseActionView(searchMenuItem); 189 | onUserNickSelected(nickName); 190 | } 191 | return true; 192 | } 193 | 194 | @Override 195 | public boolean onQueryTextChange(final String s) { 196 | final String nickName = s.trim(); 197 | if (USER_NAME_PATTERN.matcher(nickName).matches()) { 198 | onUserNickSelected(nickName); 199 | } 200 | return true; 201 | } 202 | }); 203 | MenuItemCompat.setOnActionExpandListener(searchMenuItem, new MenuItemCompat.OnActionExpandListener() { 204 | @Override 205 | public boolean onMenuItemActionExpand(final MenuItem item) { 206 | mExpandMenu = true; 207 | return true; 208 | } 209 | 210 | @Override 211 | public boolean onMenuItemActionCollapse(final MenuItem item) { 212 | mExpandMenu = false; 213 | return true; 214 | } 215 | }); 216 | if (mExpandMenu) { 217 | MenuItemCompat.expandActionView(searchMenuItem); 218 | ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)). 219 | toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_NOT_ALWAYS); 220 | } else { 221 | MenuItemCompat.collapseActionView(searchMenuItem); 222 | } 223 | } 224 | 225 | public interface OnUserSelectedListener { 226 | 227 | void onUserSelected(final User user); 228 | } 229 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/widgets/CollageItemView.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.widgets; 2 | 3 | import android.content.Context; 4 | import android.graphics.BitmapFactory; 5 | import android.graphics.Matrix; 6 | import android.graphics.drawable.Drawable; 7 | import android.os.Handler; 8 | import android.support.v4.view.GestureDetectorCompat; 9 | import android.util.AttributeSet; 10 | import android.view.GestureDetector; 11 | import android.view.MotionEvent; 12 | import android.view.ScaleGestureDetector; 13 | import android.widget.ImageView; 14 | 15 | import com.azoft.azoft.collage.R; 16 | import com.azoft.azoft.collage.data.CollageRegionData; 17 | import com.azoft.azoft.collage.utils.CollageRegion; 18 | import com.squareup.picasso.Callback; 19 | import com.squareup.picasso.Picasso; 20 | 21 | /** 22 | * Will show empty view (plus view) or image data for this region it correct place. If this view is square image is scaled and no gesture can be 23 | * made, if not, image can be moved. 24 | *

25 | * Date: 4/8/2014 26 | * Time: 5:24 PM 27 | * 28 | * @author MiG35 29 | */ 30 | @SuppressWarnings("UnusedDeclaration") 31 | public class CollageItemView extends ImageView { 32 | 33 | private static final float MIN_SCALE_FACTOR = 1f; 34 | private static final float MAX_SCALE_FACTOR = 5f; 35 | 36 | private CollageRegion mCollageRegion; 37 | private CollageRegionData mRegionData; 38 | 39 | private OnClickListener mOnClickListener; 40 | private GestureDetectorCompat mGestureDetector; 41 | private ScaleGestureDetector mScaleGestureDetector; 42 | 43 | private boolean mNeedInit = true; 44 | 45 | private float mMinScale; 46 | private float mMaxScale; 47 | private ScrollHolder mScrollHolder; 48 | 49 | public CollageItemView(final Context context) { 50 | super(context); 51 | init(context); 52 | } 53 | 54 | public CollageItemView(final Context context, final AttributeSet attrs) { 55 | super(context, attrs); 56 | init(context); 57 | } 58 | 59 | public CollageItemView(final Context context, final AttributeSet attrs, final int defStyle) { 60 | super(context, attrs, defStyle); 61 | init(context); 62 | } 63 | 64 | private void init(final Context context) { 65 | mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { 66 | private static final float HALF = 0.5f; 67 | 68 | @Override 69 | public boolean onScale(final ScaleGestureDetector detector) { 70 | final float oldScaleFactor = mRegionData.getImageScale(); 71 | // we use "+" but "*" because here it is more suitable I think 72 | float newScaleFactor = mRegionData.getImageScale() + (detector.getScaleFactor() - 1) * 0.7f; 73 | // Don't let the object get too small or too large. 74 | newScaleFactor = Math.max(MIN_SCALE_FACTOR, Math.min(newScaleFactor, MAX_SCALE_FACTOR)); 75 | 76 | if (0 != Float.compare(oldScaleFactor, newScaleFactor)) { 77 | updateImagePosition(mMinScale * oldScaleFactor, mMinScale * newScaleFactor); 78 | mRegionData.setImageScale(newScaleFactor); 79 | updateScale(); 80 | } 81 | 82 | return super.onScale(detector); 83 | } 84 | 85 | private void updateImagePosition(final float oldImageScaleFactor, final float newImageScaleFactor) { 86 | final float imageCenterX; 87 | final float imageCenterY; 88 | if (null == mRegionData.getImageLeft() || null == mRegionData.getImageTop()) { 89 | imageCenterX = (mScrollHolder.mScrollXMax + getWidth()) * HALF; 90 | imageCenterY = (mScrollHolder.mScrollYMax + getHeight()) * HALF; 91 | } else { 92 | imageCenterX = mScrollHolder.mScrollXMax * mRegionData.getImageLeft() + getWidth() * HALF; 93 | imageCenterY = mScrollHolder.mScrollYMax * mRegionData.getImageTop() + getHeight() * HALF; 94 | } 95 | final float newImageCenterX = imageCenterX * newImageScaleFactor / oldImageScaleFactor; 96 | final float newImageCenterY = imageCenterY * newImageScaleFactor / oldImageScaleFactor; 97 | 98 | final ScrollHolder scrollHolder = generateScrollHolder(newImageScaleFactor); 99 | 100 | mRegionData.setImageLeft(scrollHolder.mScrollXMax == 0 ? 0 : 101 | Math.max(0, Math.min(scrollHolder.mScrollXMax, (newImageCenterX - getWidth() * HALF) / scrollHolder.mScrollXMax))); 102 | mRegionData.setImageTop(scrollHolder.mScrollYMax == 0 ? 0 : 103 | Math.max(0, Math.min(scrollHolder.mScrollYMax, (newImageCenterY - getHeight() * HALF) / scrollHolder.mScrollYMax))); 104 | } 105 | 106 | @Override 107 | public boolean onScaleBegin(final ScaleGestureDetector detector) { 108 | return null != mScrollHolder; 109 | } 110 | }); 111 | mGestureDetector = new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener() { 112 | 113 | @Override 114 | public boolean onDown(final MotionEvent e) { 115 | return null != mOnClickListener || null != mScrollHolder; 116 | } 117 | 118 | @Override 119 | public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) { 120 | if (null != mScrollHolder) { 121 | final float curScrollX = getScrollX(); 122 | final float curScrollY = getScrollY(); 123 | 124 | scrollTo((int) (curScrollX + distanceX), (int) (curScrollY + distanceY)); 125 | } 126 | return true; 127 | } 128 | 129 | @Override 130 | public boolean onSingleTapUp(final MotionEvent e) { 131 | if (null != mOnClickListener) { 132 | mOnClickListener.onClick(CollageItemView.this); 133 | } 134 | return true; 135 | } 136 | }); 137 | } 138 | 139 | @Override 140 | public boolean onTouchEvent(final MotionEvent event) { 141 | boolean handle = false; 142 | if (null != mScrollHolder) { 143 | handle = mScaleGestureDetector.onTouchEvent(event); 144 | } 145 | handle |= mGestureDetector.onTouchEvent(event); 146 | return handle; 147 | } 148 | 149 | @Override 150 | protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) { 151 | super.onSizeChanged(w, h, oldw, oldh); 152 | 153 | if (oldw != 0 && oldh != 0) { 154 | mNeedInit = true; 155 | } 156 | 157 | new Handler().post(new Runnable() { 158 | @Override 159 | public void run() { 160 | if (mNeedInit) { 161 | setRegionData(mRegionData); 162 | } 163 | } 164 | }); 165 | } 166 | 167 | @Override 168 | public void setOnClickListener(final OnClickListener onClickListener) { 169 | mOnClickListener = onClickListener; 170 | } 171 | 172 | public void setCollageRegion(final CollageRegion collageRegion) { 173 | mCollageRegion = collageRegion; 174 | } 175 | 176 | public CollageRegion getCollageRegion() { 177 | return mCollageRegion; 178 | } 179 | 180 | public void setRegionData(final CollageRegionData regionData) { 181 | if (mRegionData != regionData) { 182 | mNeedInit = true; 183 | mRegionData = regionData; 184 | updateRegionData(); 185 | } else if (mNeedInit) { 186 | updateRegionData(); 187 | } 188 | } 189 | 190 | private void updateRegionData() { 191 | mScrollHolder = null; 192 | setImageMatrix(null); 193 | setScaleType(ScaleType.FIT_XY); 194 | 195 | if (null == mRegionData) { 196 | mNeedInit = false; 197 | setImageResource(R.drawable.ic_action_new); 198 | } else { 199 | final int width = getWidth(); 200 | final int height = getHeight(); 201 | 202 | final BitmapFactory.Options options = new BitmapFactory.Options(); 203 | options.inJustDecodeBounds = true; 204 | 205 | BitmapFactory.decodeFile(mRegionData.getImageFile().getPath(), options); 206 | 207 | if (0 == width || 0 == height || 0 == options.outWidth || 0 == options.outHeight) { 208 | return; 209 | } 210 | mNeedInit = false; 211 | 212 | final float scale = Math.max(1f * width / options.outWidth, 1f * height / options.outHeight); 213 | final int resultWidth; 214 | final int resultHeight; 215 | if (scale < 1) { 216 | resultWidth = (int) (options.outWidth * scale); 217 | resultHeight = (int) (options.outHeight * scale); 218 | } else { 219 | resultWidth = (int) (options.outWidth * Math.ceil(scale)); 220 | resultHeight = (int) (options.outHeight * Math.ceil(scale)); 221 | } 222 | 223 | Picasso.with(getContext()).load(mRegionData.getImageFile()) 224 | .resize(resultWidth, resultHeight) 225 | .skipMemoryCache().noFade() 226 | .error(R.drawable.ic_action_new).into(this, new Callback() { 227 | @Override 228 | public void onSuccess() { 229 | final int width = getWidth(); 230 | final int height = getHeight(); 231 | if (0 == width || 0 == height) { 232 | return; 233 | } 234 | final Drawable drawable = getDrawable(); 235 | if (null == drawable) { 236 | return; 237 | } 238 | final int bitmapWidth = drawable.getIntrinsicWidth(); 239 | final int bitmapHeight = drawable.getIntrinsicHeight(); 240 | if (bitmapWidth < 0 || bitmapHeight < 0) { 241 | return; 242 | } 243 | 244 | mMinScale = 1f * Math.max(1f * width / bitmapWidth, 1f * height / bitmapHeight); 245 | mMaxScale = mMinScale * MAX_SCALE_FACTOR; 246 | 247 | updateScale(); 248 | } 249 | 250 | @Override 251 | public void onError() { 252 | // pass 253 | } 254 | } 255 | ); 256 | } 257 | } 258 | 259 | private void updateScale() { 260 | final float scaleFactor = mMinScale * mRegionData.getImageScale(); 261 | mScrollHolder = generateScrollHolder(scaleFactor); 262 | final Matrix matrix = new Matrix(); 263 | matrix.setScale(scaleFactor, scaleFactor); 264 | setScaleType(ScaleType.MATRIX); 265 | setImageMatrix(matrix); 266 | 267 | if (mRegionData.getImageLeft() == null || mRegionData.getImageTop() == null) { 268 | scrollTo(mScrollHolder.mScrollXMax / 2, mScrollHolder.mScrollYMax / 2); 269 | } else { 270 | CollageItemView.super.scrollTo((int) (mRegionData.getImageLeft() * mScrollHolder.mScrollXMax), 271 | (int) (mRegionData.getImageTop() * mScrollHolder.mScrollYMax)); 272 | } 273 | } 274 | 275 | private ScrollHolder generateScrollHolder(final float scaleFactor) { 276 | final int width = getWidth(); 277 | final int height = getHeight(); 278 | if (0 == width || 0 == height) { 279 | return ScrollHolder.EMPTY; 280 | } 281 | final Drawable drawable = getDrawable(); 282 | if (null == drawable) { 283 | return ScrollHolder.EMPTY; 284 | } 285 | final int bitmapWidth = drawable.getIntrinsicWidth(); 286 | final int bitmapHeight = drawable.getIntrinsicHeight(); 287 | if (bitmapWidth < 0 || bitmapHeight < 0) { 288 | return ScrollHolder.EMPTY; 289 | } 290 | 291 | final int scrollXMax = Math.abs(Math.round(width - bitmapWidth * scaleFactor)); 292 | final int scrollYMax = Math.abs(Math.round(height - bitmapHeight * scaleFactor)); 293 | return new ScrollHolder(scrollXMax, scrollYMax); 294 | } 295 | 296 | @Override 297 | public void scrollTo(final int x, final int y) { 298 | int newScrollX = x; 299 | int newScrollY = y; 300 | if (null != mScrollHolder) { 301 | if (newScrollX > mScrollHolder.mScrollXMax) { 302 | newScrollX = mScrollHolder.mScrollXMax; 303 | } 304 | if (newScrollX < 0) { 305 | newScrollX = 0; 306 | } 307 | if (newScrollY > mScrollHolder.mScrollYMax) { 308 | newScrollY = mScrollHolder.mScrollYMax; 309 | } 310 | if (newScrollY < 0) { 311 | newScrollY = 0; 312 | } 313 | 314 | mRegionData.setImageLeft(mScrollHolder.mScrollXMax == 0 ? 0 : 1f * newScrollX / mScrollHolder.mScrollXMax); 315 | mRegionData.setImageTop(mScrollHolder.mScrollYMax == 0 ? 0 : 1f * newScrollY / mScrollHolder.mScrollYMax); 316 | } 317 | 318 | super.scrollTo(newScrollX, newScrollY); 319 | } 320 | 321 | private static class ScrollHolder { 322 | 323 | static final ScrollHolder EMPTY = new ScrollHolder(); 324 | 325 | int mScrollXMax; 326 | int mScrollYMax; 327 | 328 | ScrollHolder(final int scrollXMax, final int scrollYMax) { 329 | mScrollXMax = scrollXMax; 330 | mScrollYMax = scrollYMax; 331 | } 332 | 333 | ScrollHolder() { 334 | } 335 | } 336 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/widgets/CollageViewGroup.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.widgets; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | 8 | import com.azoft.azoft.collage.R; 9 | import com.azoft.azoft.collage.data.Collage; 10 | import com.azoft.azoft.collage.data.CollageFillData; 11 | import com.azoft.azoft.collage.data.CollageRegionData; 12 | import com.azoft.azoft.collage.utils.CollageRegion; 13 | 14 | /** 15 | * General view that construct all regions as CollageItemView children. This view calculate there sizes and location. 16 | * Can handle region clicks. 17 | *

18 | * Date: 4/8/2014 19 | * Time: 5:23 PM 20 | * 21 | * @author MiG35 22 | */ 23 | @SuppressWarnings("UnusedDeclaration") 24 | public class CollageViewGroup extends SquareRelativeLayout { 25 | 26 | private CollageFillData mCollageFillData; 27 | private final OnClickListener mCollageItemClickListener = new OnClickListener() { 28 | @Override 29 | public void onClick(final View v) { 30 | if (null != mRegionClickListener && v instanceof CollageItemView) { 31 | mRegionClickListener.onRegionClicked(((CollageItemView) v).getCollageRegion()); 32 | } 33 | } 34 | }; 35 | private RegionClickListener mRegionClickListener; 36 | 37 | public CollageViewGroup(final Context context) { 38 | super(context); 39 | } 40 | 41 | public CollageViewGroup(final Context context, final AttributeSet attrs) { 42 | super(context, attrs); 43 | } 44 | 45 | public CollageViewGroup(final Context context, final AttributeSet attrs, final int defStyle) { 46 | super(context, attrs, defStyle); 47 | } 48 | 49 | @Override 50 | protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) { 51 | super.onSizeChanged(w, h, oldw, oldh); 52 | 53 | new Handler().post(new Runnable() { 54 | @Override 55 | public void run() { 56 | setCollage(mCollageFillData); 57 | } 58 | }); 59 | } 60 | 61 | public void setCollage(final Collage collage) { 62 | setCollage(null == collage ? null : new CollageFillData(collage)); 63 | } 64 | 65 | public void setCollage(final CollageFillData collageFillData) { 66 | if (mCollageFillData != collageFillData || getChildCount() != collageFillData.getRegionsCount()) { 67 | removeAllViews(); 68 | mCollageFillData = collageFillData; 69 | initCollageViews(); 70 | } 71 | } 72 | 73 | public void invalidateRegionData() { 74 | for (int i = 0; i < getChildCount(); ++i) { 75 | final View child = getChildAt(i); 76 | if (child instanceof CollageItemView) { 77 | final CollageItemView collageItemView = (CollageItemView) child; 78 | final CollageRegionData regionData = mCollageFillData.getRegionData(collageItemView.getCollageRegion()); 79 | collageItemView.setRegionData(regionData); 80 | } 81 | } 82 | } 83 | 84 | @SuppressWarnings("ConstantConditions") 85 | private void updateClickEnable() { 86 | final boolean clicksEnabled = mRegionClickListener != null; 87 | for (int i = 0; i < getChildCount(); ++i) { 88 | final View child = getChildAt(i); 89 | if (clicksEnabled) { 90 | child.setOnClickListener(mCollageItemClickListener); 91 | } else { 92 | child.setOnClickListener(null); 93 | } 94 | } 95 | } 96 | 97 | @SuppressWarnings("ObjectAllocationInLoop") 98 | private void initCollageViews() { 99 | setBackgroundColor(getContext().getResources().getColor(R.color.collage_bg_color)); 100 | 101 | final int width = getWidth(); 102 | final int height = getHeight(); 103 | if (null == mCollageFillData || width == 0 || height == 0) { 104 | return; 105 | } 106 | for (final CollageRegion collageRegion : mCollageFillData.getCollageRegions()) { 107 | final int collageWidth = (int) Math.round(collageRegion.getWidth() * width); 108 | final int collageHeight = (int) Math.round(collageRegion.getHeight() * height); 109 | 110 | final LayoutParams layoutParams = new LayoutParams(collageWidth, collageHeight); 111 | layoutParams.setMargins((int) Math.round(width * collageRegion.getLeft()), (int) Math.round(height * collageRegion.getTop()), 0, 0); 112 | final CollageItemView collageItemView = new CollageItemView(getContext()); 113 | collageItemView.setCollageRegion(collageRegion); 114 | addView(collageItemView, layoutParams); 115 | } 116 | updateClickEnable(); 117 | invalidateRegionData(); 118 | } 119 | 120 | public void setRegionClickListener(final RegionClickListener regionClickListener) { 121 | mRegionClickListener = regionClickListener; 122 | updateClickEnable(); 123 | } 124 | 125 | public interface RegionClickListener { 126 | 127 | void onRegionClicked(CollageRegion collageRegion); 128 | } 129 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/ui/widgets/SquareRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.ui.widgets; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.RelativeLayout; 6 | 7 | /** 8 | * Help view that will be squared view with size as smallest size in measure. 9 | * Can't be used as WRAP_CONTENT on both sizes. 10 | *

11 | * Date: 4/9/2014 12 | * Time: 10:34 AM 13 | * 14 | * @author MiG35 15 | */ 16 | @SuppressWarnings("UnusedDeclaration") 17 | public class SquareRelativeLayout extends RelativeLayout { 18 | 19 | public SquareRelativeLayout(final Context context) { 20 | super(context); 21 | } 22 | 23 | public SquareRelativeLayout(final Context context, final AttributeSet attrs) { 24 | super(context, attrs); 25 | } 26 | 27 | public SquareRelativeLayout(final Context context, final AttributeSet attrs, final int defStyle) { 28 | super(context, attrs, defStyle); 29 | } 30 | 31 | @Override 32 | protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 33 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 34 | final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 35 | final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 36 | final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 37 | if (widthMode == MeasureSpec.UNSPECIFIED && heightMode == MeasureSpec.UNSPECIFIED) { 38 | throw new IllegalStateException("this view should have at least one specific or max size"); 39 | } 40 | final int neededSize; 41 | if (widthMode == MeasureSpec.UNSPECIFIED) { 42 | neededSize = heightSize; 43 | } else if (heightMode == MeasureSpec.UNSPECIFIED) { 44 | neededSize = widthSize; 45 | } else { 46 | neededSize = Math.min(widthSize, heightSize); 47 | } 48 | 49 | super.onMeasure(MeasureSpec.makeMeasureSpec(neededSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(neededSize, MeasureSpec.EXACTLY)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/utils/CollageRegion.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.utils; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Contains CollageRegion position in Collage object. All coordinates are from 0 to 1. 7 | *

8 | * Date: 4/8/2014 9 | * Time: 4:14 PM 10 | * 11 | * @author MiG35 12 | */ 13 | public class CollageRegion implements Serializable { 14 | 15 | private static final long serialVersionUID = -599596516243533187L; 16 | 17 | private final int mId; 18 | 19 | private final double mLeft; 20 | private final double mTop; 21 | private final double mRight; 22 | private final double mBottom; 23 | 24 | /** 25 | * @param id - unique region id in collage 26 | * @param left - should be in [0, 1] region. 27 | * @param top - should be in [0, 1] region. 28 | * @param right - should be in [0, 1] region. Should be greater then left. 29 | * @param bottom - should be in [0, 1] region. Should be greater then top. 30 | */ 31 | public CollageRegion(final int id, final double left, final double top, final double right, final double bottom) { 32 | //noinspection OverlyComplexBooleanExpression 33 | if (0 > left || 0 > top || left > 1 || top > 1) { 34 | throw new IllegalArgumentException("left and top positions should be from 0 to 1"); 35 | } 36 | if (left >= right || top >= bottom) { 37 | throw new IllegalArgumentException("left should be less then right, and top should be less then bottom"); 38 | } 39 | mId = id; 40 | mLeft = left; 41 | mTop = top; 42 | mRight = right; 43 | mBottom = bottom; 44 | } 45 | 46 | public int getId() { 47 | return mId; 48 | } 49 | 50 | public double getLeft() { 51 | return mLeft; 52 | } 53 | 54 | public double getTop() { 55 | return mTop; 56 | } 57 | 58 | public double getRight() { 59 | return mRight; 60 | } 61 | 62 | public double getBottom() { 63 | return mBottom; 64 | } 65 | 66 | public double getWidth() { 67 | return mRight - mLeft; 68 | } 69 | 70 | public double getHeight() { 71 | return mBottom - mTop; 72 | } 73 | 74 | @Override 75 | public boolean equals(final Object o) { 76 | if (this == o) { 77 | return true; 78 | } 79 | if (!(o instanceof CollageRegion)) { 80 | return false; 81 | } 82 | 83 | final CollageRegion otherCollageRegion = (CollageRegion) o; 84 | 85 | return mId == otherCollageRegion.mId; 86 | } 87 | 88 | @Override 89 | public int hashCode() { 90 | return mId; 91 | } 92 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/utils/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.utils; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.ResolveInfo; 6 | import android.net.ConnectivityManager; 7 | import android.net.NetworkInfo; 8 | import android.net.Uri; 9 | import android.os.Environment; 10 | 11 | import com.mig35.loaderlib.exceptions.NoNetworkException; 12 | import com.azoft.azoft.collage.R; 13 | import com.azoft.azoft.collage.app.CollageApplication; 14 | import com.azoft.azoft.collage.exceptions.DiskWriteException; 15 | import com.azoft.azoft.collage.exceptions.InternalServerException; 16 | 17 | import java.io.BufferedInputStream; 18 | import java.io.BufferedOutputStream; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.util.List; 24 | 25 | public final class CommonUtils { 26 | 27 | public static File getCacheFileDir() { 28 | File rootCacheDir = null; 29 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 30 | rootCacheDir = CollageApplication.getInstance().getExternalCacheDir(); 31 | } 32 | if (checkAndCreateDirIfNotExists(rootCacheDir)) { 33 | return rootCacheDir; 34 | } 35 | rootCacheDir = getInternalCacheDir(); 36 | if (checkAndCreateDirIfNotExists(rootCacheDir)) { 37 | return rootCacheDir; 38 | } 39 | 40 | return null; 41 | } 42 | 43 | public static File getInternalCacheDir() { 44 | final File rootCacheDir = CollageApplication.getInstance().getCacheDir(); 45 | 46 | if (checkAndCreateDirIfNotExists(rootCacheDir)) { 47 | return rootCacheDir; 48 | } 49 | 50 | return null; 51 | } 52 | 53 | public static File getInternalFilesDir() { 54 | final File rootFilesDir = CollageApplication.getInstance().getFilesDir(); 55 | 56 | if (checkAndCreateDirIfNotExists(rootFilesDir)) { 57 | return rootFilesDir; 58 | } 59 | 60 | return null; 61 | } 62 | 63 | public static boolean checkAndCreateDirIfNotExists(final File rootCacheDir) { 64 | if (null != rootCacheDir) { 65 | if (!rootCacheDir.exists() && rootCacheDir.mkdirs()) { 66 | return true; 67 | } 68 | 69 | if (rootCacheDir.exists()) { 70 | return true; 71 | } 72 | } 73 | return false; 74 | } 75 | 76 | /** 77 | * Will search connected or connecting network. If found do nothing, if not, 78 | * throws exception 79 | * 80 | * @throws java.io.IOException if no network found 81 | */ 82 | public static void checkInternet() throws IOException { 83 | final ConnectivityManager cm = (ConnectivityManager) CollageApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE); 84 | final NetworkInfo netInfo = cm.getActiveNetworkInfo(); 85 | if (netInfo == null || !netInfo.isConnectedOrConnecting()) { 86 | throw new NoNetworkException(); 87 | } 88 | } 89 | 90 | public static void writeNetworkStreamToAnOtherStream(final InputStream inputStream, final OutputStream outputStream) 91 | throws DiskWriteException, InternalServerException, IOException { 92 | if (null == inputStream || null == outputStream) { 93 | throw new IllegalArgumentException("input and output streams can't be null"); 94 | } 95 | 96 | boolean read = true; 97 | try { 98 | try { 99 | final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); 100 | final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); 101 | final byte[] buffer = new byte[2048]; 102 | int readCount; 103 | while ((readCount = bufferedInputStream.read(buffer)) != -1) { 104 | read = false; 105 | bufferedOutputStream.write(buffer, 0, readCount); 106 | read = true; 107 | } 108 | bufferedOutputStream.flush(); 109 | } finally { 110 | inputStream.close(); 111 | } 112 | } catch (final IOException e) { 113 | if (read) { 114 | checkInternet(); 115 | throw new InternalServerException(e); 116 | } else { 117 | throw new DiskWriteException(e); 118 | } 119 | } finally { 120 | outputStream.close(); 121 | } 122 | } 123 | 124 | private CommonUtils() { 125 | } 126 | 127 | @SuppressWarnings("ConstantConditions") 128 | public static void sendEmailWithAttachment(final Context context, final String path, final String emailSubject) { 129 | final Uri uriAsPath = Uri.parse(path); 130 | final Intent emailIntent = new Intent(Intent.ACTION_SEND); 131 | emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 132 | emailIntent.putExtra(Intent.EXTRA_STREAM, uriAsPath); 133 | emailIntent.putExtra(Intent.EXTRA_SUBJECT, emailSubject); 134 | emailIntent.putExtra(Intent.EXTRA_TEXT, emailSubject); 135 | emailIntent.setType("image/*"); 136 | final List emailActivities = context.getPackageManager().queryIntentActivities(emailIntent, 0); 137 | // we should grand all interested apps read permission 138 | for (final ResolveInfo resolveInfo : emailActivities) { 139 | if (null != resolveInfo.activityInfo) { 140 | context.grantUriPermission(resolveInfo.activityInfo.packageName, uriAsPath, Intent.FLAG_GRANT_READ_URI_PERMISSION); 141 | } 142 | } 143 | 144 | emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 145 | context.startActivity(Intent.createChooser(emailIntent, context.getString(R.string.text_email_chooser))); 146 | } 147 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/utils/MediaUtils.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.utils; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | import com.azoft.azoft.collage.exceptions.DiskWriteException; 6 | 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | 12 | /** 13 | * Helper class to store bitmap as file with provider access. 14 | *

15 | * Date: 4/10/2014 16 | * Time: 3:24 PM 17 | * 18 | * @author MiG35 19 | */ 20 | public final class MediaUtils { 21 | 22 | private static final String STORAGE_FILE_PATH = "mediadata"; 23 | 24 | private static final String STORAGE_AUTHORITY = "com.azoft.azoft.collage.contentproviders.media"; 25 | private static final String STORAGE_AUTHORITY_PATH = "images"; 26 | 27 | private MediaUtils() { 28 | } 29 | 30 | public static String insertImage(final Bitmap source, final String title) throws DiskWriteException { 31 | final File storageFile = getStoreFile(title); 32 | if (null == storageFile) { 33 | return null; 34 | } 35 | try { 36 | final OutputStream imageOut = new FileOutputStream(storageFile); 37 | try { 38 | source.compress(Bitmap.CompressFormat.PNG, 100, imageOut); 39 | } finally { 40 | imageOut.close(); 41 | } 42 | return "content://" + STORAGE_AUTHORITY + '/' + STORAGE_AUTHORITY_PATH + '/' + title; 43 | } catch (final IOException e) { 44 | throw new DiskWriteException(e); 45 | } 46 | } 47 | 48 | static File getStoreFile(final String title) { 49 | final File internalFilesDir = CommonUtils.getInternalFilesDir(); 50 | if (null == internalFilesDir) { 51 | return null; 52 | } 53 | final File mediaDataDir = new File(internalFilesDir, STORAGE_FILE_PATH); 54 | if (!mediaDataDir.exists() && !mediaDataDir.mkdir()) { 55 | return null; 56 | } 57 | return new File(mediaDataDir, title); 58 | } 59 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/utils/collagegenerators/CollageFactory.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.utils.collagegenerators; 2 | 3 | import com.azoft.azoft.collage.data.Collage; 4 | 5 | /** 6 | * This class helps to create collages from it's number. 7 | *

8 | * Date: 4/8/2014 9 | * Time: 4:46 PM 10 | * 11 | * @author MiG35 12 | */ 13 | public interface CollageFactory { 14 | 15 | Collage getCollage(final int number); 16 | 17 | int getCollageCount(); 18 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/java/com/azoft/azoft/collage/utils/collagegenerators/SimpleCollageGenerator.java: -------------------------------------------------------------------------------- 1 | package com.azoft.azoft.collage.utils.collagegenerators; 2 | 3 | import android.util.SparseArray; 4 | 5 | import com.azoft.azoft.collage.data.Collage; 6 | import com.azoft.azoft.collage.utils.CollageRegion; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Generator with hardcoded numbers of collagees. 13 | *

14 | * Date: 4/8/2014 15 | * Time: 4:20 PM 16 | * 17 | * @author MiG35 18 | */ 19 | public final class SimpleCollageGenerator implements CollageFactory { 20 | 21 | private static final int KOLAJ_ITEMS_COUNT = 4; 22 | private final SparseArray mCollages = new SparseArray<>(); 23 | 24 | @Override 25 | public Collage getCollage(final int number) { 26 | if (number < 0 || number >= 4) { 27 | throw new IllegalArgumentException("SimpleCollageGenerator can create collagees from 0 to 3 items only"); 28 | } 29 | Collage collage = mCollages.get(number); 30 | if (null == collage) { 31 | collage = generateCollage(number); 32 | mCollages.put(number, collage); 33 | } 34 | return collage; 35 | } 36 | 37 | @Override 38 | public int getCollageCount() { 39 | return KOLAJ_ITEMS_COUNT; 40 | } 41 | 42 | @SuppressWarnings({"ValueOfIncrementOrDecrementUsed", "MagicNumber"}) 43 | private Collage generateCollage(final int number) { 44 | final List collageRegions = new ArrayList<>(); 45 | int regionId = 0; 46 | if (0 == number) { 47 | collageRegions.add(new CollageRegion(regionId, 0.01, 0.01, 0.99, 0.99)); 48 | } else if (1 == number) { 49 | collageRegions.add(new CollageRegion(regionId++, 0.01, 0.01, 0.49d, 0.99)); 50 | collageRegions.add(new CollageRegion(regionId, 0.51d, 0.01, 0.99, 0.99)); 51 | } else if (2 == number) { 52 | collageRegions.add(new CollageRegion(regionId++, 0.01, 0.01, 0.49d, 0.99)); 53 | collageRegions.add(new CollageRegion(regionId++, 0.51d, 0.01, 0.99, 0.49d)); 54 | collageRegions.add(new CollageRegion(regionId, 0.51d, 0.51d, 0.99, 0.99)); 55 | } else if (3 == number) { 56 | collageRegions.add(new CollageRegion(regionId++, 0.01, 0.01, 0.49d, 0.49d)); 57 | collageRegions.add(new CollageRegion(regionId++, 0.01, 0.51d, 0.49d, 0.99)); 58 | collageRegions.add(new CollageRegion(regionId++, 0.51d, 0.01, 0.99, 0.49d)); 59 | collageRegions.add(new CollageRegion(regionId, 0.51d, 0.51d, 0.99, 0.99)); 60 | } 61 | return new Collage(collageRegions); 62 | } 63 | } -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-hdpi/ic_action_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-hdpi/ic_action_picture.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-hdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-hdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-hdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-hdpi/ic_action_share.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-hdpi/ic_action_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-hdpi/ic_action_user.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-hdpi/selected_collage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-hdpi/selected_collage.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-mdpi/ic_action_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-mdpi/ic_action_picture.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-mdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-mdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-mdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-mdpi/ic_action_share.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-mdpi/ic_action_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-mdpi/ic_action_user.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-mdpi/selected_collage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-mdpi/selected_collage.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_picture.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_share.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xhdpi/ic_action_user.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xhdpi/selected_collage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xhdpi/selected_collage.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xxhdpi/ic_action_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xxhdpi/ic_action_picture.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xxhdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xxhdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xxhdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xxhdpi/ic_action_share.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/drawable/ic_action_new.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azoft/CollageMaker-Android/353328896b64f31b3438ea6459f98fc5e9524efa/Azoft-Collage/src/main/res/drawable/ic_action_new.9.png -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/layout/activity_instagram_auth.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/layout/activity_single_fragment.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Azoft-Collage/src/main/res/layout/activity_start.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 |