33 |
34 | #### Contributing
35 | ## This project is no longer maintained
36 |
37 | ###### Code
38 | If you are a developer and you wish to contribute to the app please fork the project
39 | and submit a pull request on the [dev branch](https://gitlab.com/HoraApps/LeafPic/tree/dev).
40 |
41 | ###### Issues
42 | You can trace the status of known issues [here](https://gitlab.com/HoraApps/LeafPic/issues),
43 | also feel free to file a new issue (helpful description, screenshots and logcat are appreciated), or send me an [email](mailto:dnld.sht@gmail.com) if you have any questions.
44 |
45 | ###### Translations
46 | If you are able to contribute with a new translation of a missing language or if you want to improve an existing one, we greatly appreciate any suggestion!
47 | The project uses [Crowdin](https://crowdin.com/project/leafpic), a platform that allows anybody to contribute to translating the app
48 |
49 | #### Licensing
50 | LeafPic is licensed under the [GNU v3 Public License](https://gitlab.com/HoraApps/LeafPic/blob/dev/LICENSE).
51 | In addition to the terms set by the GNU v3 Public License, we ask that if you use any code from this repository that you send us a message to let us know.
52 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /src/main/gen/
3 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/dnld/Android/Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep fullSizeOptions here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 | -keep class butterknife.** { *; }
19 | -dontwarn butterknife.internal.**
20 | -keep class **$$ViewBinder { *; }
21 |
22 | -keepclasseswithmembernames class * {
23 | @butterknife.* ;
24 | }
25 |
26 | -keepclasseswithmembernames class * {
27 | @butterknife.* ;
28 | }
29 |
30 | -keepnames class * { @butterknife.Bind *;}
31 |
32 | -dontwarn okio.**
--------------------------------------------------------------------------------
/app/src/main/assets/latest_changelog.md:
--------------------------------------------------------------------------------
1 | v0.6-beta-1
2 | ==================
3 | - Fixed crash on startup and some random crash
4 | - Fixed crash opening video (Nougat)
5 | - fixed zoom out issue with SubScaling ImageView enabled
6 | - Updated translations
7 | - General improvements
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/App.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic;
2 |
3 | import android.support.multidex.MultiDexApplication;
4 |
5 | import com.mikepenz.community_material_typeface_library.CommunityMaterial;
6 | import com.mikepenz.fontawesome_typeface_library.FontAwesome;
7 | import com.mikepenz.google_material_typeface_library.GoogleMaterial;
8 | import com.mikepenz.iconics.Iconics;
9 | import com.orhanobut.hawk.Hawk;
10 | import com.squareup.leakcanary.LeakCanary;
11 |
12 | import org.horaapps.leafpic.util.ApplicationUtils;
13 | import org.horaapps.leafpic.util.preferences.Prefs;
14 |
15 | /**
16 | * Created by dnld on 28/04/16.
17 | */
18 | public class App extends MultiDexApplication {
19 |
20 | private static App mInstance;
21 |
22 | @Override
23 | public void onCreate() {
24 | super.onCreate();
25 | mInstance = this;
26 |
27 | ApplicationUtils.init(this);
28 |
29 | /** This process is dedicated to LeakCanary for heap analysis.
30 | * You should not init your app in this process. */
31 | if (LeakCanary.isInAnalyzerProcess(this)) {
32 | return;
33 | }
34 | LeakCanary.install(this);
35 |
36 | registerFontIcons();
37 | initialiseStorage();
38 | }
39 |
40 | public static App getInstance() {
41 | return mInstance;
42 | }
43 |
44 | private void registerFontIcons() {
45 | Iconics.registerFont(new GoogleMaterial());
46 | Iconics.registerFont(new CommunityMaterial());
47 | Iconics.registerFont(new FontAwesome());
48 | }
49 |
50 | private void initialiseStorage() {
51 | Prefs.init(this);
52 | Hawk.init(this).build();
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/CardViewStyle.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic;
2 |
3 |
4 | /**
5 | * Created by Jibo on 20/11/2016.
6 | */
7 | public enum CardViewStyle {
8 |
9 | MATERIAL(0, R.layout.card_album_material),
10 | FLAT(1, R.layout.card_album_flat),
11 | COMPACT(2, R.layout.card_album_compact);
12 |
13 | private static final int size = CardViewStyle.values().length;
14 |
15 | int value;
16 | int layout;
17 |
18 | CardViewStyle(int value, int layout) {
19 | this.value = value;
20 | this.layout = layout;
21 | }
22 |
23 | public int getLayout() {
24 | return layout;
25 | }
26 |
27 | public int getValue() { return value; }
28 |
29 | public static int getSize() {
30 | return size;
31 | }
32 |
33 | public static CardViewStyle fromValue(int value){
34 | switch (value){
35 | case 0: default: return MATERIAL;
36 | case 1: return FLAT;
37 | case 2: return COMPACT;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/CustomGlideModule.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic;
2 |
3 | import android.content.Context;
4 |
5 | import com.bumptech.glide.Glide;
6 | import com.bumptech.glide.GlideBuilder;
7 | import com.bumptech.glide.Registry;
8 |
9 | /**
10 | * Created by dnld on 10/03/16.
11 | */
12 |
13 | public class CustomGlideModule implements com.bumptech.glide.module.GlideModule {
14 |
15 | @Override
16 | public void registerComponents(Context context, Glide glide, Registry registry) {
17 |
18 | }
19 |
20 | @Override
21 | public void applyOptions(Context context, GlideBuilder builder) {
22 | // Apply options to the builder here.
23 | /*builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
24 |
25 | MemorySizeCalculator calculator = new MemorySizeCalculator(context);
26 | int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
27 | int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
28 |
29 | int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
30 | int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
31 |
32 | builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
33 | builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
34 |
35 | int cacheSize100MegaBytes = 104857600;
36 |
37 | builder.setDiskCache(
38 | new InternalCacheDiskCacheFactory(context, cacheSize100MegaBytes)
39 | );*/
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/SecretConstants.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.Properties;
8 |
9 | /**
10 | * Created by dnld on 31/07/16.
11 | */
12 |
13 | public class SecretConstants {
14 |
15 | private static String base64EncodedPublicKey;
16 |
17 | public static String MAP_BOX_TOKEN = "pk.eyJ1IjoiZG5sZCIsImEiOiJjaXJycmVham4wMGRsaGpuaHQ4Y3Fhb2MzIn0.kUn2aNbfpS3-wDJ-s0DLFw";
18 |
19 | public static String getBase64EncodedPublicKey(Context context) {
20 | if (base64EncodedPublicKey == null) {
21 | InputStream input;
22 | try {
23 | input = context.getAssets().open("secretconstants.properties");
24 | Properties properties = new Properties();
25 | properties.load(input);
26 | base64EncodedPublicKey = properties.getProperty("gplaykey");
27 | } catch (IOException e) {
28 | // file not found
29 | base64EncodedPublicKey = "";
30 | }
31 |
32 | }
33 | return base64EncodedPublicKey;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/about/Contact.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.about;
2 |
3 | /**
4 | * Created by dnld on 04/03/18.
5 | */
6 |
7 | public class Contact {
8 | private String value;
9 | private String label;
10 |
11 | public Contact(String value, String label) {
12 | this.value = value;
13 | this.label = label;
14 | }
15 |
16 | public String getLabel() {
17 | return label;
18 | }
19 |
20 | public void setLabel(String label) {
21 | this.label = label;
22 | }
23 |
24 | public String getValue() {
25 | return value;
26 | }
27 |
28 | public void setValue(String value) {
29 | this.value = value;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/about/ContactButton.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.about;
2 |
3 | import android.content.Context;
4 | import android.graphics.Typeface;
5 | import android.support.annotation.NonNull;
6 | import android.view.ViewGroup;
7 | import android.widget.LinearLayout;
8 |
9 | import org.horaapps.leafpic.R;
10 | import org.horaapps.liz.ThemeHelper;
11 | import org.horaapps.liz.ui.ThemedTextView;
12 |
13 | /**
14 | * Created by dnld on 04/03/18.
15 | */
16 |
17 | public class ContactButton extends ThemedTextView {
18 |
19 | public ContactButton(Context context) {
20 | super(context);
21 | setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
22 | int padd = (int) context.getResources().getDimension(R.dimen.developer_links_small_padding);
23 | setPadding(padd, padd, padd, padd);
24 | setTextSize(16);
25 | }
26 |
27 | public void bold() {
28 | setTypeface(null, Typeface.BOLD);
29 | }
30 |
31 | @Override
32 | public void refreshTheme(ThemeHelper theme) {
33 | setTextColor(theme.getAccentColor());
34 | }
35 |
36 | public void setText(@NonNull String text) {
37 | super.setText(text.toUpperCase());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/about/ContactListener.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.about;
2 |
3 | /**
4 | * Created by dnld on 04/03/18.
5 | */
6 |
7 | public interface ContactListener {
8 | void onContactClicked(Contact contact);
9 | void onMailClicked(String mail);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/about/Contributor.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.about;
2 |
3 | import android.support.annotation.DrawableRes;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * Created by dnld on 04/03/18.
9 | */
10 |
11 | public class Contributor {
12 | private String name;
13 | private String description;
14 | private String email;
15 | private @DrawableRes
16 | int profileImage;
17 |
18 | private ArrayList contacts;
19 |
20 | public Contributor(String name, String description, @DrawableRes int profileImage) {
21 | this.name = name;
22 | this.description = description;
23 | this.profileImage = profileImage;
24 | this.contacts = new ArrayList<>();
25 | }
26 |
27 | public void setEmail(String email) {
28 | this.email = email;
29 | }
30 |
31 | public void addSocial(String label, String url) {
32 | Contact c = new Contact(url, label);
33 | contacts.add(c);
34 | }
35 |
36 | public String getEmail() {
37 | return email;
38 | }
39 |
40 | public String getName() {
41 | return name;
42 | }
43 |
44 | public String getDescription() {
45 | return description;
46 | }
47 |
48 | public int getProfileImage() {
49 | return profileImage;
50 | }
51 |
52 | public ArrayList getContacts() {
53 | return contacts;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/about/ContributorViewHolder.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.about;
2 |
3 | import android.support.annotation.DrawableRes;
4 | import android.support.annotation.Nullable;
5 | import android.view.View;
6 | import android.widget.LinearLayout;
7 | import android.widget.TextView;
8 |
9 | import org.horaapps.leafpic.R;
10 | import org.horaapps.liz.ThemeHelper;
11 | import org.horaapps.liz.ThemedViewHolder;
12 |
13 | import java.util.ArrayList;
14 |
15 | import butterknife.BindView;
16 | import butterknife.ButterKnife;
17 | import de.hdodenhof.circleimageview.CircleImageView;
18 |
19 | /**
20 | * Custom ViewHolder for populating a Contributor onto {@link ContributorsAdapter}
21 | */
22 | public class ContributorViewHolder extends ThemedViewHolder {
23 |
24 | @BindView(R.id.contributor_profile_image) CircleImageView profileImage;
25 | @BindView(R.id.contributor_name) TextView contribName;
26 | @BindView(R.id.contributor_description) TextView contribDescription;
27 | @BindView(R.id.contributor_contacts) LinearLayout contribContacts;
28 |
29 | public ContributorViewHolder(View view) {
30 | super(view);
31 | ButterKnife.bind(this, itemView);
32 | }
33 |
34 | private void setProfileImage(@DrawableRes int profileImage) {
35 | this.profileImage.setImageResource(profileImage);
36 | }
37 |
38 | private void setName(@Nullable String name) {
39 | contribName.setText(name);
40 | }
41 |
42 | private void setDescription(@Nullable String description) {
43 | contribDescription.setText(description);
44 | }
45 |
46 | @Override
47 | public void refreshTheme(ThemeHelper themeHelper) {
48 | super.refreshTheme(themeHelper);
49 | int borderColor = themeHelper.getInvertedBackgroundColor();
50 | profileImage.setBorderColor(borderColor);
51 | }
52 |
53 | public void load(Contributor contributor, ContactListener listener) {
54 | setName(contributor.getName());
55 | setDescription(contributor.getDescription());
56 | setProfileImage(contributor.getProfileImage());
57 |
58 | contribContacts.removeAllViews();
59 |
60 | if (contributor.getEmail() != null) {
61 | ContactButton email = new ContactButton(itemView.getContext());
62 | email.setText(itemView.getContext().getString(R.string.send_email));
63 | email.bold();
64 | email.setOnClickListener(v -> listener.onMailClicked(contributor.getEmail()));
65 | contribContacts.addView(email);
66 | }
67 |
68 | ArrayList contacts = contributor.getContacts();
69 | for (Contact contact : contacts) {
70 | ContactButton c = new ContactButton(itemView.getContext());
71 | c.setText(contact.getLabel());
72 | c.setOnClickListener(v -> listener.onContactClicked(contact));
73 | contribContacts.addView(c);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/about/ContributorsAdapter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.about;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.ViewGroup;
6 |
7 | import org.horaapps.leafpic.R;
8 | import org.horaapps.liz.ThemedAdapter;
9 |
10 | import java.util.ArrayList;
11 |
12 | /**
13 | * Created by dnld on 04/03/18.
14 | */
15 |
16 | public class ContributorsAdapter extends ThemedAdapter {
17 |
18 | private ContactListener listener;
19 | private ArrayList contributors;
20 |
21 | ContributorsAdapter(Context context, ArrayList contributors, ContactListener listener) {
22 | super(context);
23 | this.contributors = contributors;
24 | this.listener = listener;
25 | }
26 |
27 | @Override
28 | public ContributorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
29 | return new ContributorViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_about_contributor, parent, false));
30 | }
31 |
32 | @Override
33 | public void onBindViewHolder(ContributorViewHolder holder, int position) {
34 | Contributor contributor = contributors.get(position);
35 | holder.load(contributor, listener);
36 | super.onBindViewHolder(holder, position);
37 | }
38 |
39 | @Override
40 | public int getItemCount() {
41 | return contributors.size();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/activities/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.activities.base;
2 |
3 | import android.support.annotation.CallSuper;
4 |
5 | import org.horaapps.liz.ThemedActivity;
6 |
7 | import io.reactivex.disposables.CompositeDisposable;
8 | import io.reactivex.disposables.Disposable;
9 |
10 | public abstract class BaseActivity extends ThemedActivity {
11 | CompositeDisposable disposables = new CompositeDisposable();
12 |
13 |
14 | public void disposeLater(Disposable disposable) {
15 | disposables.add(disposable);
16 | }
17 |
18 | @CallSuper
19 | @Override
20 | protected void onDestroy() {
21 | disposables.dispose();
22 | super.onDestroy();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/activities/base/SharedMediaActivity.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.activities.base;
2 |
3 | import android.content.DialogInterface;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.support.v7.app.AlertDialog;
7 | import android.widget.Toast;
8 |
9 | import org.horaapps.leafpic.R;
10 | import org.horaapps.leafpic.data.StorageHelper;
11 | import org.horaapps.leafpic.util.AlertDialogsHelper;
12 |
13 | /**
14 | * Created by dnld on 03/08/16.
15 | */
16 |
17 | public abstract class SharedMediaActivity extends BaseActivity {
18 |
19 | private int REQUEST_CODE_SD_CARD_PERMISSIONS = 42;
20 |
21 | public void requestSdCardPermissions() {
22 | AlertDialog textDialog = AlertDialogsHelper.getTextDialog(this, R.string.sd_card_write_permission_title, R.string.sd_card_permissions_message);
23 | textDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.ok_action).toUpperCase(), new DialogInterface.OnClickListener() {
24 | @Override
25 | public void onClick(DialogInterface dialogInterface, int i) {
26 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP)
27 | startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), REQUEST_CODE_SD_CARD_PERMISSIONS);
28 | }
29 | });
30 | textDialog.show();
31 | }
32 |
33 | @Override
34 | public void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) {
35 | if (resultCode == RESULT_OK) {
36 | if (requestCode == REQUEST_CODE_SD_CARD_PERMISSIONS) {
37 | Uri treeUri = resultData.getData();
38 | // Persist URI in shared preference so that you can use it later.
39 | StorageHelper.saveSdCardInfo(getApplicationContext(), treeUri);
40 | getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
41 | Toast.makeText(this, R.string.got_permission_wr_sdcard, Toast.LENGTH_SHORT).show();
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/adapters/MediaPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.adapters;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentManager;
6 | import android.support.v4.app.FragmentStatePagerAdapter;
7 | import android.support.v4.view.PagerAdapter;
8 | import android.util.SparseArray;
9 | import android.view.ViewGroup;
10 |
11 | import org.horaapps.leafpic.data.Media;
12 | import org.horaapps.leafpic.fragments.GifFragment;
13 | import org.horaapps.leafpic.fragments.ImageFragment;
14 | import org.horaapps.leafpic.fragments.VideoFragment;
15 |
16 | import java.util.ArrayList;
17 |
18 | /**
19 | * Created by dnld on 18/02/16.
20 | */
21 |
22 | public class MediaPagerAdapter extends FragmentStatePagerAdapter {
23 |
24 | private final String TAG = "asd";
25 | private ArrayList media;
26 | private SparseArray registeredFragments = new SparseArray<>();
27 |
28 | public MediaPagerAdapter(FragmentManager fm, ArrayList media) {
29 | super(fm);
30 | this.media = media;
31 | }
32 |
33 | @Override
34 | public Fragment getItem(int pos) {
35 | Media media = this.media.get(pos);
36 | if (media.isVideo()) return VideoFragment.newInstance(media);
37 | if (media.isGif()) return GifFragment.newInstance(media);
38 | else return ImageFragment.newInstance(media);
39 | }
40 |
41 | @NonNull
42 | @Override
43 | public Object instantiateItem(ViewGroup container, int position) {
44 | Fragment fragment = (Fragment) super.instantiateItem(container, position);
45 | registeredFragments.put(position, fragment);
46 | return fragment;
47 | }
48 |
49 | @Override
50 | public void destroyItem(ViewGroup container, int position, Object object) {
51 | registeredFragments.remove(position);
52 | super.destroyItem(container, position, object);
53 | }
54 |
55 | public Fragment getRegisteredFragment(int position) {
56 | return registeredFragments.get(position);
57 | }
58 |
59 | public void swapDataSet(ArrayList media) {
60 | this.media = media;
61 | notifyDataSetChanged();
62 | }
63 |
64 | @Override
65 | public int getItemPosition(@NonNull Object object) {
66 | return PagerAdapter.POSITION_NONE;
67 | }
68 |
69 | @Override
70 | public int getCount() {
71 | return media.size();
72 | }
73 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/animations/DepthPageTransformer.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.animations;
2 |
3 | import android.support.v4.view.ViewPager;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by dnld on 1/18/16.
8 | */
9 | public class DepthPageTransformer implements ViewPager.PageTransformer {
10 | private static final float MIN_SCALE = 0.75f;
11 |
12 | public void transformPage(View view, float position) {
13 | int pageWidth = view.getWidth();
14 |
15 | if (position < -1) { // [-Infinity,-1)
16 | // This page is way off-screen to the left.
17 | view.setAlpha(0);
18 |
19 | } else if (position <= 0) { // [-1,0]
20 | // Use the default slide transition when moving to the left page
21 | view.setAlpha(1);
22 | view.setTranslationX(0);
23 | view.setScaleX(1);
24 | view.setScaleY(1);
25 |
26 | } else if (position <= 1) { // (0,1]
27 | // Fade the page out.
28 | view.setAlpha(1 - position);
29 |
30 | // Counteract the default slide transition
31 | view.setTranslationX(pageWidth * -position);
32 |
33 | // Scale the page down (between MIN_SCALE and 1)
34 | float scaleFactor = MIN_SCALE
35 | + (1 - MIN_SCALE) * (1 - Math.abs(position));
36 | view.setScaleX(scaleFactor);
37 | view.setScaleY(scaleFactor);
38 |
39 | } else { // (1,+Infinity]
40 | // This page is way off-screen to the right.
41 | view.setAlpha(0);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/AlbumSettings.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import org.horaapps.leafpic.data.filter.FilterMode;
7 | import org.horaapps.leafpic.data.sort.SortingMode;
8 | import org.horaapps.leafpic.data.sort.SortingOrder;
9 |
10 | import java.io.Serializable;
11 |
12 | /**
13 | * Created by dnld on 2/4/16.
14 | */
15 | public class AlbumSettings implements Serializable, Parcelable {
16 |
17 | String coverPath;
18 | int sortingMode, sortingOrder;
19 | boolean pinned;
20 | FilterMode filterMode = FilterMode.ALL;
21 |
22 | public static AlbumSettings getDefaults() {
23 | return new AlbumSettings(null, SortingMode.DATE.getValue(), 1, 0);
24 | }
25 |
26 | AlbumSettings(String cover, int sortingMode, int sortingOrder, int pinned) {
27 | this.coverPath = cover;
28 | this.sortingMode = sortingMode;
29 | this.sortingOrder = sortingOrder;
30 | this.pinned = pinned == 1;
31 | }
32 |
33 | public SortingMode getSortingMode() {
34 | return SortingMode.fromValue(sortingMode);
35 | }
36 |
37 | public SortingOrder getSortingOrder() {
38 | return SortingOrder.fromValue(sortingOrder);
39 | }
40 |
41 | @Override
42 | public int describeContents() {
43 | return 0;
44 | }
45 |
46 | @Override
47 | public void writeToParcel(Parcel dest, int flags) {
48 | dest.writeString(this.coverPath);
49 | dest.writeInt(this.sortingMode);
50 | dest.writeInt(this.sortingOrder);
51 | dest.writeByte(this.pinned ? (byte) 1 : (byte) 0);
52 | dest.writeInt(this.filterMode == null ? -1 : this.filterMode.ordinal());
53 | }
54 |
55 | /** This is the constructor used by CREATOR. */
56 | protected AlbumSettings(Parcel in) {
57 | this.coverPath = in.readString();
58 | this.sortingMode = in.readInt();
59 | this.sortingOrder = in.readInt();
60 | this.pinned = in.readByte() != 0;
61 | int tmpFilterMode = in.readInt();
62 | this.filterMode = tmpFilterMode == -1 ? null : FilterMode.values()[tmpFilterMode];
63 | }
64 |
65 | /** It is a non-null static field that must be in parcelable. */
66 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
67 |
68 | @Override
69 | public AlbumSettings createFromParcel(Parcel source) {
70 | return new AlbumSettings(source);
71 | }
72 |
73 | @Override
74 | public AlbumSettings[] newArray(int size) {
75 | return new AlbumSettings[size];
76 | }
77 | };
78 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/CursorHandler.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data;
2 |
3 | import android.database.Cursor;
4 |
5 | /**
6 | * Created by dnld on 3/13/17.
7 | */
8 |
9 | public interface CursorHandler {
10 |
11 | T handle(Cursor cu);
12 | static String [] getProjection() {
13 | return new String[0];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/IAlbum.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data;
2 |
3 | /**
4 | * Created by dnld on 6/28/17.
5 | */
6 |
7 | public interface IAlbum {
8 | String getName();
9 | String getPath();
10 | int getCount();
11 | Media getCover();
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/filter/FilterMode.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.filter;
2 |
3 | /**
4 | * Created by dnld on 18/08/16.
5 | */
6 |
7 | public enum FilterMode {
8 | ALL,
9 | IMAGES,
10 | GIF,
11 | VIDEO,
12 | NO_VIDEO
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/filter/FoldersFileFilter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.filter;
2 |
3 | import java.io.File;
4 | import java.io.FileFilter;
5 |
6 | /**
7 | * Created by dnld on 24/04/16.
8 | */
9 | public class FoldersFileFilter implements FileFilter {
10 | @Override
11 | public boolean accept(File pathname) {
12 | return pathname.isDirectory();
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/filter/IMediaFilter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.filter;
2 |
3 | import org.horaapps.leafpic.data.Media;
4 |
5 | /**
6 | * Created by dnld on 4/10/17.
7 | */
8 |
9 | public interface IMediaFilter {
10 | boolean accept(Media media);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/filter/ImageFileFilter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.filter;
2 |
3 | import java.io.File;
4 | import java.io.FilenameFilter;
5 | import java.util.regex.Pattern;
6 |
7 | /**
8 | * Created by dnld on 24/04/16.
9 | */
10 | public class ImageFileFilter implements FilenameFilter {
11 |
12 | private Pattern pattern;
13 |
14 | public ImageFileFilter(boolean includeVideo) {
15 | pattern = includeVideo
16 | ? Pattern.compile(".(jpg|png|gif|jpe|jpeg|bmp|webp|mp4|mkv|webm|avi)$", Pattern.CASE_INSENSITIVE)
17 | : Pattern.compile(".(jpg|png|gif|jpe|jpeg|bmp|webp)$", Pattern.CASE_INSENSITIVE);
18 | }
19 |
20 | @Override
21 | public boolean accept(File dir, String filename) {
22 | return new File(dir, filename).isFile() && pattern.matcher(filename).find();
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/filter/MediaFilter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.filter;
2 |
3 | import org.horaapps.leafpic.data.Media;
4 |
5 | /**
6 | * Created by dnld on 4/10/17.
7 | */
8 | public class MediaFilter {
9 | public static IMediaFilter getFilter(FilterMode mode) {
10 | switch (mode) {
11 | case ALL: default:
12 | return media -> true;
13 | case GIF:
14 | return Media::isGif;
15 | case VIDEO:
16 | return Media::isVideo;
17 | case IMAGES: return Media::isImage;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/filter/NotHiddenFoldersFilter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.filter;
2 |
3 | import java.io.File;
4 | import java.io.FileFilter;
5 |
6 | /**
7 | * Created by dnld on 24/04/16.
8 | */
9 | public class NotHiddenFoldersFilter implements FileFilter {
10 | @Override
11 | public boolean accept(File file) {
12 | return file.isDirectory() && !file.isHidden() && !new File(file, ".nomedia").exists();
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/metadata/MediaDetailsMap.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.metadata;
2 |
3 | import java.util.HashMap;
4 | import java.util.TreeMap;
5 |
6 | /**
7 | * Created by dnld on 16/08/16.
8 | */
9 |
10 | public class MediaDetailsMap extends HashMap {
11 |
12 | private TreeMap keys;
13 |
14 | public MediaDetailsMap() {
15 | super();
16 | keys = new TreeMap<>();
17 | }
18 |
19 | public String getValue(int index) {
20 | String key = keys.get(index);
21 | return super.get(key);
22 | }
23 |
24 | public String getLabel(int index) {
25 | return keys.get(index);
26 | }
27 |
28 | @Override
29 | public String put(String key, String value) {
30 | keys.put(keys.size(), key);
31 | return super.put(key, value);
32 | }
33 |
34 | public int[] getKeySet() {
35 | int[] array = new int[keys.size()];
36 | for(int i = 0; i < keys.size(); i++) array[i]=i;
37 | return array;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/provider/ContentProviderHelper.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.provider;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.provider.MediaStore;
6 |
7 | import org.horaapps.leafpic.data.Album;
8 |
9 | import java.io.File;
10 |
11 | /**
12 | * Created by dnld on 24/07/16.
13 | */
14 |
15 | @Deprecated
16 | public class ContentProviderHelper {
17 |
18 | @Deprecated
19 | public static long getAlbumId(Context context, String mediaPath) {
20 | long id = -1;
21 | Cursor cur = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),
22 | new String[]{ MediaStore.Files.FileColumns.PARENT },
23 | MediaStore.Files.FileColumns.DATA+"=?", new String[]{ mediaPath }, null);
24 |
25 | if(cur != null && cur.moveToFirst()){
26 | id = cur.getLong(0);
27 | cur.close();
28 | }
29 |
30 | return id;
31 | }
32 |
33 | @Deprecated
34 | public static Album getAlbumFromMedia(Context context, String mediaPath) {
35 | File parentFolder = new File(mediaPath).getParentFile();
36 | if (parentFolder == null || !parentFolder.isDirectory())
37 | return null;
38 |
39 | return new Album(context, parentFolder.getPath(), getAlbumId(context, mediaPath), parentFolder.getName(), 0);
40 | }
41 | }
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/provider/QueryUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.provider;
2 |
3 | import android.content.ContentResolver;
4 | import android.database.Cursor;
5 |
6 | import org.horaapps.leafpic.data.CursorHandler;
7 |
8 | import io.reactivex.Observable;
9 |
10 |
11 | /**
12 | * Created by dnld on 3/13/17.
13 | */
14 |
15 | public class QueryUtils {
16 |
17 | public static Observable query(Query q, ContentResolver cr, CursorHandler ch) {
18 | return Observable.create(subscriber -> {
19 | Cursor cursor = null;
20 | try {
21 | cursor = q.getCursor(cr);
22 | if (cursor != null && cursor.getCount() > 0)
23 | while (cursor.moveToNext()) subscriber.onNext(ch.handle(cursor));
24 | subscriber.onComplete();
25 | }
26 | catch (Exception err) { subscriber.onError(err); }
27 | finally { if (cursor != null) cursor.close(); }
28 | });
29 | }
30 |
31 | /**
32 | * return only the first element if there is one
33 | *
34 | * @param q
35 | * @param cr
36 | * @param ch
37 | * @param
38 | * @return
39 | */
40 | public static Observable querySingle(Query q, ContentResolver cr, CursorHandler ch) {
41 | return Observable.create(subscriber -> {
42 | Cursor cursor = null;
43 | try {
44 | cursor = q.getCursor(cr);
45 | if (cursor != null && cursor.moveToFirst())
46 | subscriber.onNext(ch.handle(cursor));
47 | subscriber.onComplete();
48 | }
49 | catch (Exception err) { subscriber.onError(err); }
50 | finally { if (cursor != null) cursor.close(); }
51 | });
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/sort/MediaComparators.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.sort;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import org.horaapps.leafpic.data.AlbumSettings;
6 | import org.horaapps.leafpic.data.Media;
7 | import org.horaapps.leafpic.timeline.data.TimelineHeaderModel;
8 | import org.horaapps.leafpic.util.NumericComparator;
9 |
10 | import java.util.Comparator;
11 |
12 | /**
13 | * Created by dnld on 26/04/16.
14 | */
15 |
16 | public class MediaComparators {
17 |
18 | public static Comparator getComparator(AlbumSettings settings) {
19 | return getComparator(settings.getSortingMode(), settings.getSortingOrder());
20 | }
21 |
22 | public static Comparator getComparator(SortingMode sortingMode, SortingOrder sortingOrder) {
23 | return sortingOrder == SortingOrder.ASCENDING
24 | ? getComparator(sortingMode) : reverse(getComparator(sortingMode));
25 | }
26 |
27 | public static Comparator getTimelineComparator(@NonNull SortingOrder sortingOrder) {
28 | return sortingOrder.isAscending() ? getTimelineComparator() : reverse(getTimelineComparator());
29 | }
30 |
31 | public static Comparator getComparator(SortingMode sortingMode) {
32 | switch (sortingMode) {
33 | case NAME: return getNameComparator();
34 | case DATE: default: return getDateComparator();
35 | case SIZE: return getSizeComparator();
36 | case TYPE: return getTypeComparator();
37 | case NUMERIC: return getNumericComparator();
38 | }
39 | }
40 |
41 | private static Comparator reverse(Comparator comparator) {
42 | return (o1, o2) -> comparator.compare(o2, o1);
43 | }
44 |
45 | private static Comparator getDateComparator() {
46 | return (f1, f2) -> f1.getDateModified().compareTo(f2.getDateModified());
47 | }
48 |
49 | private static Comparator getNameComparator() {
50 | return (f1, f2) -> f1.getPath().compareTo(f2.getPath());
51 | }
52 |
53 | private static Comparator getSizeComparator() {
54 | return (f1, f2) -> Long.compare(f1.getSize(), f2.getSize());
55 | }
56 |
57 | private static Comparator getTypeComparator() {
58 | return (f1, f2) -> f1.getMimeType().compareTo(f2.getMimeType());
59 | }
60 |
61 | private static Comparator getNumericComparator() {
62 | return (f1, f2) -> NumericComparator.filevercmp(f1.getPath(), f2.getPath());
63 | }
64 |
65 | private static Comparator getTimelineComparator() {
66 | return (t1, t2) -> t1.getDate().compareTo(t2.getDate());
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/sort/SortingMode.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.sort;
2 |
3 | import android.provider.MediaStore;
4 |
5 | /**
6 | * Created by dnld on 18/08/16.
7 | */
8 |
9 | public enum SortingMode {
10 | NAME (0, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME),
11 | DATE (1, MediaStore.MediaColumns.DATE_MODIFIED, "max(" + MediaStore.Images.Media.DATE_MODIFIED + ")"),
12 | SIZE(2, MediaStore.MediaColumns.SIZE, "count(*)"),
13 | TYPE(3, MediaStore.MediaColumns.MIME_TYPE),
14 | NUMERIC(4, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME);
15 |
16 | int value;
17 | String mediaColumn;
18 | String albumsColumn;
19 |
20 | SortingMode(int value, String mediaColumn) {
21 | this.value = value;
22 | this.mediaColumn = mediaColumn;
23 | this.albumsColumn = mediaColumn;
24 | }
25 |
26 | SortingMode(int value, String mediaColumn, String albumsColumn) {
27 | this.value = value;
28 | this.mediaColumn = mediaColumn;
29 | this.albumsColumn = albumsColumn;
30 | }
31 |
32 | public String getMediaColumn() {
33 | return mediaColumn;
34 | }
35 |
36 | public String getAlbumsColumn() {
37 | return albumsColumn;
38 | }
39 |
40 | public int getValue() {
41 | return value;
42 | }
43 |
44 | public static SortingMode fromValue(int value) {
45 | switch (value) {
46 | case 0: return NAME;
47 | case 1: default: return DATE;
48 | case 2: return SIZE;
49 | case 3: return TYPE;
50 | case 4: return NUMERIC;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/data/sort/SortingOrder.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.data.sort;
2 |
3 | /**
4 | * Created by dnld on 18/08/16.
5 | */
6 |
7 | public enum SortingOrder {
8 | ASCENDING(1), DESCENDING(0);
9 |
10 | int value;
11 |
12 | SortingOrder(int value) {
13 | this.value = value;
14 | }
15 |
16 | public int getValue() {
17 | return value;
18 | }
19 |
20 | public boolean isAscending() {
21 | return value == ASCENDING.getValue();
22 | }
23 |
24 | public static SortingOrder fromValue(boolean value) {
25 | return value ? ASCENDING : DESCENDING;
26 | }
27 |
28 | public static SortingOrder fromValue(int value) {
29 | return value == 0 ? DESCENDING : ASCENDING;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | import android.content.Context;
4 |
5 | import org.horaapps.liz.ThemedFragment;
6 |
7 | /**
8 | * Base Fragment for abstraction logic.
9 | */
10 | public abstract class BaseFragment extends ThemedFragment {
11 |
12 | private NothingToShowListener nothingToShowListener;
13 |
14 | @Override
15 | public void onAttach(Context context) {
16 | super.onAttach(context);
17 |
18 | if (context instanceof NothingToShowListener)
19 | nothingToShowListener = (NothingToShowListener) context;
20 | }
21 |
22 | public NothingToShowListener getNothingToShowListener() {
23 | return nothingToShowListener;
24 | }
25 |
26 | public void setNothingToShowListener(NothingToShowListener nothingToShowListener) {
27 | this.nothingToShowListener = nothingToShowListener;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/BaseMediaFragment.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.NonNull;
6 | import android.view.View;
7 |
8 | import org.horaapps.leafpic.data.Media;
9 | import org.horaapps.liz.ThemeHelper;
10 |
11 | /**
12 | * A Base Fragment for showing Media.
13 | */
14 | public abstract class BaseMediaFragment extends BaseFragment {
15 |
16 | private static final String ARGS_MEDIA = "args_media";
17 |
18 | protected Media media;
19 | private MediaTapListener mediaTapListener;
20 |
21 | @NonNull
22 | protected static T newInstance(@NonNull T mediaFragment,
23 | @NonNull Media media) {
24 |
25 | Bundle args = new Bundle();
26 | args.putParcelable(ARGS_MEDIA, media);
27 | mediaFragment.setArguments(args);
28 | return mediaFragment;
29 | }
30 |
31 | @Override
32 | public void onAttach(Context context) {
33 | super.onAttach(context);
34 | if (context instanceof MediaTapListener) mediaTapListener = (MediaTapListener) context;
35 | }
36 |
37 | private void fetchArgs() {
38 | Bundle args = getArguments();
39 | if (args == null) throw new RuntimeException("Must pass arguments to Media Fragments!");
40 | media = getArguments().getParcelable(ARGS_MEDIA);
41 | }
42 |
43 | @Override
44 | public void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | fetchArgs();
47 | }
48 |
49 | @Override
50 | public void refreshTheme(ThemeHelper themeHelper) {
51 | // Default implementation
52 | }
53 |
54 | protected void setTapListener(@NonNull View view) {
55 | view.setOnClickListener(v -> onTapped());
56 | }
57 |
58 | private void onTapped() {
59 | mediaTapListener.onViewTapped();
60 | }
61 |
62 | /**
63 | * Interface for listeners to react on Media Clicks.
64 | */
65 | public interface MediaTapListener {
66 |
67 | /**
68 | * Called when user taps on the Media view.
69 | */
70 | void onViewTapped();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/BaseMediaGridFragment.kt:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments
2 |
3 | import android.content.Context
4 | import android.view.View
5 |
6 | import org.horaapps.leafpic.items.ActionsListener
7 |
8 | /**
9 | * Base class for fragments showing any kind of Media in a Grid fashion.
10 | *
11 | * Allows selection, multiple select Context Menus, etc.
12 | */
13 | abstract class BaseMediaGridFragment : BaseFragment(), IFragment, ActionsListener {
14 |
15 | lateinit var editModeListener: EditModeListener
16 |
17 | override fun onAttach(context: Context?) {
18 | super.onAttach(context)
19 | if (context is EditModeListener) editModeListener = context
20 | else throw RuntimeException("Parent must implement Edit Mode Listener!")
21 | }
22 |
23 | fun onBackPressed() = when (editMode()) {
24 | true -> {
25 | exitContextMenu()
26 | true
27 | }
28 | false -> false
29 | }
30 |
31 | /**
32 | * Exit the Context Menu.
33 | */
34 | protected fun exitContextMenu() {
35 | clearSelected()
36 | updateToolbar()
37 | }
38 |
39 | /**
40 | * Update the Toolbar for switching between Edit Mode.
41 | */
42 | protected fun updateToolbar() {
43 | editModeListener.changedEditMode(
44 | editMode(),
45 | getSelectedCount(),
46 | getTotalCount(),
47 | getToolbarButtonListener(editMode()),
48 | getToolbarTitle())
49 |
50 | // Refresh the Toolbar menu
51 | activity?.invalidateOptionsMenu()
52 | }
53 |
54 | /**
55 | * The total selected item count.
56 | */
57 | abstract fun getSelectedCount(): Int
58 |
59 | /**
60 | * The total number of items displayed.
61 | */
62 | abstract fun getTotalCount(): Int
63 |
64 | /**
65 | * A listener to be invoked when user taps on the Toolbar icon.
66 | */
67 | abstract fun getToolbarButtonListener(editMode: Boolean): View.OnClickListener?
68 |
69 | /**
70 | * Text to display on the toolbar.
71 | */
72 | abstract fun getToolbarTitle(): String?
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/EditModeListener.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | import android.view.View;
4 |
5 | import javax.annotation.Nullable;
6 |
7 | /**
8 | * Created by dnld on 12/16/17.
9 | */
10 |
11 | public interface EditModeListener {
12 |
13 | /**
14 | * Propagate Edit Mode switches to listeners.
15 | *
16 | * @param editMode Whether we are in Edit Mode or not.
17 | * @param selected The number of items selected.
18 | * @param total The total number of items.
19 | * @param listener The listener for Toolbar Back Button presses.
20 | * @param title The Toolbar's title.
21 | */
22 | void changedEditMode(boolean editMode, int selected, int total, @Nullable View.OnClickListener listener, @Nullable String title);
23 |
24 | /**
25 | * Propagate the selected item count to listeners.
26 | *
27 | * @param count The number of items selected.
28 | * @param total The total number of items.
29 | */
30 | void onItemsSelected(int count, int total);
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/GifFragment.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import org.horaapps.leafpic.data.Media;
10 |
11 | import pl.droidsonroids.gif.GifImageView;
12 |
13 | /**
14 | * Media Fragment for showing an Image (static)
15 | */
16 | public class GifFragment extends BaseMediaFragment {
17 |
18 | @NonNull
19 | public static GifFragment newInstance(@NonNull Media media) {
20 | return BaseMediaFragment.newInstance(new GifFragment(), media);
21 | }
22 |
23 | @Override
24 | public View onCreateView(@NonNull LayoutInflater inflater,
25 | ViewGroup container,
26 | Bundle savedInstanceState) {
27 |
28 | GifImageView photoView = new GifImageView(getContext());
29 | photoView.setImageURI(media.getUri());
30 | setTapListener(photoView);
31 | return photoView;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/IFragment.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | /**
4 | * Created by dnld on 3/24/17.
5 | */
6 |
7 | public interface IFragment {
8 |
9 | boolean editMode();
10 | boolean clearSelected();
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/ImageFragment.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | import android.net.Uri;
4 | import android.os.Bundle;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import com.davemorrissey.labs.subscaleview.ImageSource;
12 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
13 |
14 | import org.horaapps.leafpic.R;
15 | import org.horaapps.leafpic.data.Media;
16 | import org.horaapps.leafpic.util.BitmapUtils;
17 |
18 | import butterknife.BindView;
19 | import butterknife.ButterKnife;
20 |
21 | /**
22 | * A Media Fragment for showing an Image (static)
23 | */
24 | public class ImageFragment extends BaseMediaFragment {
25 |
26 | @BindView(R.id.subsampling_view) SubsamplingScaleImageView imageView;
27 |
28 | @NonNull
29 | public static ImageFragment newInstance(@NonNull Media media) {
30 | return BaseMediaFragment.newInstance(new ImageFragment(), media);
31 | }
32 |
33 | @Override
34 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
35 | View rootView = inflater.inflate(R.layout.fragment_photo, container, false);
36 | ButterKnife.bind(this, rootView);
37 | return rootView;
38 | }
39 |
40 | @Override
41 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
42 | super.onViewCreated(view, savedInstanceState);
43 | Uri mediaUri = media.getUri();
44 | imageView.setOrientation(BitmapUtils.getOrientation(mediaUri, getContext()));
45 | imageView.setImage(ImageSource.uri(mediaUri));
46 | setTapListener(imageView);
47 | }
48 |
49 | @Override
50 | public void onDestroyView() {
51 | imageView.recycle();
52 | super.onDestroyView();
53 | }
54 |
55 | /**
56 | * Rotate the currently displaying media image.
57 | *
58 | * @param rotationInDegrees The rotation in degrees
59 | */
60 | public void rotatePicture(int rotationInDegrees) {
61 | if (rotationInDegrees == -90 && imageView.getOrientation() == 0) imageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_270);
62 | else imageView.setOrientation(Math.abs(imageView.getOrientation() + rotationInDegrees) % 360);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/NothingToShowListener.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | /**
4 | * Created by dnld on 12/16/17.
5 | */
6 |
7 | public interface NothingToShowListener {
8 |
9 | void changedNothingToShow(boolean nothingToShow);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/fragments/VideoFragment.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.fragments;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.ImageView;
12 |
13 | import com.bumptech.glide.Glide;
14 | import com.bumptech.glide.load.engine.DiskCacheStrategy;
15 | import com.bumptech.glide.request.RequestOptions;
16 |
17 | import org.horaapps.leafpic.R;
18 | import org.horaapps.leafpic.data.Media;
19 | import org.horaapps.leafpic.data.StorageHelper;
20 | import org.horaapps.liz.ThemeHelper;
21 | import org.horaapps.liz.ui.ThemedIcon;
22 |
23 | import butterknife.BindView;
24 | import butterknife.ButterKnife;
25 |
26 | /**
27 | * A Media Fragment for showing a Video Preview.
28 | */
29 | public class VideoFragment extends BaseMediaFragment {
30 |
31 | @BindView(R.id.media_view) ImageView previewView;
32 | @BindView(R.id.video_play_icon) ThemedIcon playVideoIcon;
33 |
34 | @NonNull
35 | public static VideoFragment newInstance(@NonNull Media media) {
36 | return BaseMediaFragment.newInstance(new VideoFragment(), media);
37 | }
38 |
39 | @Override
40 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
41 | View rootView = inflater.inflate(R.layout.fragment_video, container, false);
42 | ButterKnife.bind(this, rootView);
43 | return rootView;
44 | }
45 |
46 | @Override
47 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
48 | super.onViewCreated(view, savedInstanceState);
49 |
50 | playVideoIcon.setOnClickListener(v -> {
51 | Uri uri = StorageHelper.getUriForFile(getContext(), media.getFile());
52 | Intent intent = new Intent(Intent.ACTION_VIEW).setDataAndType(uri, media.getMimeType());
53 | intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
54 | startActivity(intent);
55 | });
56 |
57 | // TODO: See where we can move this. Seems like boilerplate code that belongs in
58 | // a utility class or Builder of some sort.
59 | RequestOptions options =
60 | new RequestOptions().signature(media.getSignature()).centerCrop()
61 | .diskCacheStrategy(
62 | DiskCacheStrategy.AUTOMATIC);
63 |
64 | Glide.with(getContext()).load(media.getUri()).apply(options).into(previewView);
65 | setTapListener(previewView);
66 | }
67 |
68 | @Override
69 | public void refreshTheme(ThemeHelper themeHelper) {
70 | playVideoIcon.refreshTheme(themeHelper);
71 | }
72 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/interfaces/MediaClickListener.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.interfaces;
2 |
3 | import org.horaapps.leafpic.data.Album;
4 | import org.horaapps.leafpic.data.Media;
5 |
6 | import java.util.ArrayList;
7 |
8 | public interface MediaClickListener {
9 |
10 | void onMediaClick(Album album, ArrayList media, int position);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/items/ActionsListener.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.items;
2 |
3 | /**
4 | * Created by dnld on 10/03/18.
5 | */
6 |
7 | /**
8 | * Interface for listeners to be alerted when this entity wants to
9 | * perform some actions on items.
10 | */
11 | public interface ActionsListener {
12 |
13 | /**
14 | * Used when the user clicks on an item.
15 | *
16 | * @param position The position that was clicked.
17 | */
18 | void onItemSelected(int position);
19 |
20 | /**
21 | * Use to toggle Select Mode states
22 | *
23 | * @param selectMode Whether we want to be in select mode or not.
24 | */
25 | void onSelectMode(boolean selectMode);
26 |
27 | /**
28 | * Used to notify listeners about selection counts.
29 | *
30 | * @param selectionCount The number of selected items
31 | * @param totalCount The number of total items
32 | */
33 | void onSelectionCountChanged(int selectionCount, int totalCount);
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/progress/ErrorCause.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.progress;
2 |
3 | import android.support.annotation.Nullable;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class ErrorCause {
8 |
9 | private String title;
10 | private ArrayList causes;
11 |
12 | public ErrorCause(String title, ArrayList causes) {
13 | this.title = title;
14 | this.causes = causes;
15 | }
16 |
17 | public ErrorCause(String title) {
18 | this.title = title;
19 | this.causes = new ArrayList<>(1);
20 | }
21 |
22 | public void addCause(String cause) {
23 | this.causes.add(cause);
24 | }
25 |
26 | public String getTitle() {
27 | return title;
28 | }
29 |
30 | public boolean hasErrors() {
31 | return causes.size() > 0;
32 | }
33 |
34 | public @Nullable
35 | ErrorCause get() {
36 | if (hasErrors()) return this;
37 | else return null;
38 | }
39 |
40 | public ArrayList getCauses() {
41 | return causes;
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | StringBuilder b = new StringBuilder();
47 | b.append(title).append("\n");
48 |
49 | for (String cause : causes) {
50 | b.append(cause).append("\n");
51 | }
52 |
53 | return b.toString();
54 | }
55 |
56 | public static ErrorCause fromThrowable(Throwable throwable) {
57 | if (throwable instanceof ProgressException)
58 | return ((ProgressException) throwable).getError();
59 | else return new ErrorCause(throwable.getMessage());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/progress/ErrorCauseAdapter.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.progress;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.view.LayoutInflater;
6 | import android.view.ViewGroup;
7 |
8 | import org.horaapps.leafpic.R;
9 | import org.horaapps.liz.ThemedAdapter;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | public class ErrorCauseAdapter extends ThemedAdapter {
15 |
16 | private List errors;
17 |
18 | public ErrorCauseAdapter(Context context, List errors) {
19 | super(context);
20 | this.errors = errors;
21 | }
22 |
23 | public void setErrors(ArrayList errors) {
24 | this.errors = errors;
25 | notifyDataSetChanged();
26 | }
27 |
28 | @NonNull
29 | @Override
30 | public ErrorCauseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
31 | return new ErrorCauseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_error_cause, parent, false));
32 | }
33 |
34 | @Override
35 | public void onBindViewHolder(@NonNull ErrorCauseViewHolder holder, int position) {
36 | holder.load(errors.get(position));
37 | super.onBindViewHolder(holder, position);
38 | }
39 |
40 | @Override
41 | public int getItemCount() {
42 | return errors.size();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/progress/ErrorCauseViewHolder.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.progress;
2 |
3 | import android.view.View;
4 |
5 | import org.horaapps.leafpic.R;
6 | import org.horaapps.liz.ThemedViewHolder;
7 | import org.horaapps.liz.ui.ThemedLinearLayout;
8 | import org.horaapps.liz.ui.ThemedTextView;
9 |
10 | import butterknife.BindView;
11 | import butterknife.ButterKnife;
12 |
13 | public class ErrorCauseViewHolder extends ThemedViewHolder {
14 |
15 | @BindView(R.id.error_title)
16 | ThemedTextView title;
17 |
18 | @BindView(R.id.error_causes)
19 | ThemedLinearLayout causes;
20 |
21 | ErrorCauseViewHolder(View itemView) {
22 | super(itemView);
23 | ButterKnife.bind(this, itemView);
24 | }
25 |
26 | public void load(ErrorCause errorCause) {
27 | title.setText(errorCause.getTitle());
28 |
29 | causes.removeAllViews();
30 | for (String c : errorCause.getCauses()) {
31 | ThemedTextView textView = new ThemedTextView(itemView.getContext());
32 | textView.setStyleColor(ThemedTextView.SUB_TEXT_COLOR);
33 | //textView.setTextSize();
34 | textView.setText(c);
35 | causes.addView(textView);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/progress/ProgressException.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.progress;
2 |
3 | public class ProgressException extends Exception {
4 |
5 | private ErrorCause error;
6 |
7 | public ProgressException(ErrorCause error) {
8 | this.error = error;
9 | }
10 |
11 | public ProgressException(String error) {
12 | this.error = new ErrorCause(error);
13 | }
14 |
15 | public ErrorCause getError() {
16 | return error;
17 | }
18 |
19 | @Override
20 | public String toString() {
21 | return error.toString();
22 | }
23 |
24 | @Override
25 | public String getMessage() {
26 | return toString();
27 | }
28 |
29 | @Override
30 | public String getLocalizedMessage() {
31 | return toString();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/settings/ThemedSetting.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.settings;
2 |
3 | import org.horaapps.liz.ThemedActivity;
4 |
5 | /**
6 | * Created by dnld on 12/9/16.
7 | */
8 |
9 | class ThemedSetting {
10 |
11 | private ThemedActivity activity;
12 |
13 | ThemedSetting(ThemedActivity activity) {
14 | this.activity = activity;
15 | }
16 |
17 | public ThemedSetting() {
18 | }
19 |
20 | public ThemedActivity getActivity() {
21 | return activity;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/timeline/data/TimelineHeaderModel.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.timeline.data;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import java.util.Calendar;
7 | import java.util.Date;
8 | import java.util.GregorianCalendar;
9 |
10 | /**
11 | * Model for showing the Timeline headers.
12 | */
13 | public class TimelineHeaderModel implements TimelineItem {
14 |
15 | private Calendar calendar;
16 | private String headerText;
17 |
18 | public TimelineHeaderModel(@NonNull Date date) {
19 | this(date.getTime());
20 | }
21 |
22 | public TimelineHeaderModel(long timeInMillis) {
23 | calendar = new GregorianCalendar();
24 | calendar.setTimeInMillis(timeInMillis);
25 | }
26 |
27 | public TimelineHeaderModel(@NonNull Calendar calendar) {
28 | this.calendar = calendar;
29 | }
30 |
31 | public void setHeaderText(@NonNull String headerText) {
32 | this.headerText = headerText;
33 | }
34 |
35 | @NonNull
36 | public Calendar getDate() {
37 | return calendar;
38 | }
39 |
40 | @Nullable
41 | public String getHeaderText() {
42 | return headerText;
43 | }
44 |
45 | @Override
46 | public int getTimelineType() {
47 | return TYPE_HEADER;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/timeline/data/TimelineItem.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.timeline.data;
2 |
3 | import android.support.annotation.IntDef;
4 |
5 | /**
6 | * Interface to define that this item is capable of being displayed on timeline
7 | */
8 | public interface TimelineItem {
9 |
10 | int TYPE_HEADER = 101;
11 | int TYPE_MEDIA = 102;
12 |
13 | @IntDef({TYPE_HEADER, TYPE_MEDIA})
14 | @interface TimelineItemType {}
15 |
16 | @TimelineItemType
17 | int getTimelineType();
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/AnimationUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.support.v4.view.ViewPager;
4 | import android.support.v7.widget.RecyclerView;
5 |
6 | import org.horaapps.leafpic.util.preferences.Prefs;
7 |
8 | /**
9 | * Created by dnld on 24/02/18.
10 | */
11 |
12 | public class AnimationUtils {
13 |
14 | public static RecyclerView.ItemAnimator getItemAnimator(RecyclerView.ItemAnimator itemAnimator) {
15 | if(Prefs.animationsEnabled()) {
16 | return itemAnimator;
17 | }
18 | return null;
19 | }
20 |
21 | public static ViewPager.PageTransformer getPageTransformer(ViewPager.PageTransformer pageTransformer) {
22 | if(Prefs.animationsEnabled()) {
23 | return pageTransformer;
24 | }
25 | return null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/ApplicationUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 |
6 | import org.horaapps.leafpic.BuildConfig;
7 |
8 | /**
9 | * Data class for holding Application-related data.
10 | */
11 | public class ApplicationUtils {
12 |
13 | private static String PACKAGE_NAME;
14 |
15 | public static void init(@NonNull Context context) {
16 | PACKAGE_NAME = context.getPackageName();
17 | }
18 |
19 | /**
20 | * Get the Application's package name specified in Manifest
21 | */
22 | @NonNull
23 | public static String getPackageName() {
24 | return PACKAGE_NAME;
25 | }
26 |
27 | @NonNull
28 | public static String getAppVersion() {
29 | return BuildConfig.VERSION_NAME;
30 | }
31 |
32 | public static boolean isDebug() {
33 | return BuildConfig.DEBUG;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/ArrayUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | /**
6 | * All kinds of Array helpers belong here
7 | */
8 | public final class ArrayUtils {
9 |
10 | /**
11 | * Find the index of an element in an array.
12 | * Performs a linear search across the array.
13 | *
14 | * @param array The array to search
15 | * @param element The element to find
16 | * @return The position of element in array, else -1 if not found.
17 | */
18 | public static int getIndex(@NonNull T[] array, @NonNull T element) {
19 | for (int pos = 0; pos < array.length; pos++) {
20 | if (array[pos].equals(element)) return pos;
21 | }
22 | return -1;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.net.Uri;
8 | import android.support.media.ExifInterface;
9 |
10 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
11 |
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 |
15 | /**
16 | * Created by dnld on 3/25/17.
17 | */
18 |
19 | public class BitmapUtils {
20 |
21 | public static Bitmap addWhiteBorder(Bitmap bmp, int borderSize) {
22 | Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize * 2, bmp.getHeight() + borderSize * 2, bmp.getConfig());
23 | Canvas canvas = new Canvas(bmpWithBorder);
24 | canvas.drawColor(Color.WHITE);
25 | canvas.drawBitmap(bmp, borderSize, borderSize, null);
26 | return bmpWithBorder;
27 | }
28 |
29 | public static Bitmap getCroppedBitmap(Bitmap srcBmp){
30 | Bitmap dstBmp;
31 | if (srcBmp.getWidth() >= srcBmp.getHeight()){
32 | dstBmp = Bitmap.createBitmap(srcBmp,
33 | srcBmp.getWidth()/2 - srcBmp.getHeight()/2, 0,
34 | srcBmp.getHeight(), srcBmp.getHeight()
35 | );
36 | } else {
37 | dstBmp = Bitmap.createBitmap(srcBmp, 0,
38 | srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
39 | srcBmp.getWidth(), srcBmp.getWidth()
40 | );
41 | }
42 | return dstBmp;
43 | }
44 |
45 | public static int getOrientation(Uri uri, Context ctx){
46 |
47 | try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
48 | if (in == null) {
49 | return 0;
50 | }
51 | ExifInterface exif = new ExifInterface(in);
52 | int orientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, 1 );
53 |
54 | switch ( orientation ) {
55 | case ExifInterface.ORIENTATION_ROTATE_180:
56 | return SubsamplingScaleImageView.ORIENTATION_180;
57 | case ExifInterface.ORIENTATION_ROTATE_90:
58 | return SubsamplingScaleImageView.ORIENTATION_90;
59 | case ExifInterface.ORIENTATION_ROTATE_270:
60 | return SubsamplingScaleImageView.ORIENTATION_270;
61 | default:
62 | return SubsamplingScaleImageView.ORIENTATION_0;
63 | }
64 | } catch ( IOException e ) {
65 | return 0;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/ChromeCustomTabs.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Context;
5 | import android.net.Uri;
6 | import android.support.annotation.ColorInt;
7 | import android.support.annotation.NonNull;
8 | import android.support.customtabs.CustomTabsClient;
9 | import android.support.customtabs.CustomTabsIntent;
10 | import android.support.customtabs.CustomTabsServiceConnection;
11 |
12 | import org.horaapps.liz.ThemeHelper;
13 |
14 | /**
15 | * A Chrome Custom Tabs wrapper to preload and show URLs in Chrome Custom Tabs.
16 | * Also provides a static method to launch a tab directly without warm up.
17 | */
18 | public class ChromeCustomTabs {
19 |
20 | private CustomTabsServiceConnection serviceConnection;
21 | private CustomTabsIntent mCustomTabsIntent;
22 |
23 | @ColorInt private int toolbarColor;
24 | private Context context;
25 |
26 | public ChromeCustomTabs(@NonNull Context context) {
27 | this.context = context;
28 | toolbarColor = ThemeHelper.getInstance(context).getPrimaryColor();
29 | initService();
30 | }
31 |
32 | private void initService() {
33 |
34 | serviceConnection = new CustomTabsServiceConnection() {
35 | @Override
36 | public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {
37 | customTabsClient.warmup(0L);
38 | }
39 |
40 | @Override
41 | public void onServiceDisconnected(ComponentName name) {
42 | // NO-OP
43 | }
44 | };
45 |
46 | // Bind the Chrome Custom Tabs service
47 | CustomTabsClient.bindCustomTabsService(context, ApplicationUtils.getPackageName(), serviceConnection);
48 |
49 | mCustomTabsIntent = new CustomTabsIntent.Builder()
50 | .setShowTitle(true)
51 | .setToolbarColor(toolbarColor)
52 | .build();
53 | }
54 |
55 | public void launchUrl(String Url) {
56 | mCustomTabsIntent.launchUrl(context, Uri.parse(Url));
57 | }
58 |
59 | /**
60 | * Allow the Chrome Custom Tabs service to disconnect and GC.
61 | */
62 | public void destroy() {
63 | context.unbindService(serviceConnection);
64 | }
65 |
66 | /**
67 | * Launches a Chrome Custom Tab without warmup / service.
68 | *
69 | * @param context The context - used for launching an Activity.
70 | * @param url The URL to load.
71 | */
72 | public static void launchUrl(@NonNull Context context, @NonNull String url) {
73 | CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build();
74 | customTabsIntent.launchUrl(context, Uri.parse(url));
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/DeviceUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.content.res.Configuration;
4 | import android.content.res.Resources;
5 | import android.support.annotation.NonNull;
6 |
7 | /**
8 | * Utility class for accessing Android device-specific information
9 | */
10 | public class DeviceUtils {
11 |
12 | /**
13 | * Returns the state of device being in Landscape orientation.
14 | */
15 | public static boolean isLandscape(@NonNull Resources resources) {
16 | return resources.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
17 | }
18 |
19 | /**
20 | * Returns the state of device being in Portrait orientation.
21 | */
22 | public static boolean isPortrait(@NonNull Resources resources) {
23 | return resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/FingerPrint.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.app.KeyguardManager;
4 | import android.content.Context;
5 | import android.hardware.fingerprint.FingerprintManager;
6 | import android.os.Build;
7 | import android.support.annotation.RequiresApi;
8 |
9 | import org.horaapps.leafpic.R;
10 |
11 | import static android.content.Context.FINGERPRINT_SERVICE;
12 | import static android.content.Context.KEYGUARD_SERVICE;
13 |
14 | /**
15 | * Created by gilbert on 24/03/2017.
16 | */
17 |
18 | public class FingerPrint {
19 |
20 | @RequiresApi(api = Build.VERSION_CODES.M)
21 | public static boolean checkFinger(Context ctx) {
22 |
23 | // Keyguard Manager
24 | KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE);
25 | // Fingerprint Manager
26 | FingerprintManager fingerprintManager = (FingerprintManager) ctx.getSystemService(FINGERPRINT_SERVICE);
27 |
28 | try {
29 | // Check if the fingerprint sensor is present
30 | if (!fingerprintManager.isHardwareDetected()) {
31 | // Update the UI with a message
32 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_supported));
33 | return false;
34 | }
35 |
36 | if (!fingerprintManager.hasEnrolledFingerprints()) {
37 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_configured));
38 | return false;
39 | }
40 |
41 | if (!keyguardManager.isKeyguardSecure()) {
42 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_enabled_sls));
43 | return false;
44 | }
45 | }
46 | catch(SecurityException se) {
47 | se.printStackTrace();
48 | }
49 | return true;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/GLideRotateTransformation.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Matrix;
5 | import android.support.annotation.NonNull;
6 |
7 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
8 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
9 |
10 | import java.security.MessageDigest;
11 |
12 | /**
13 | * Created by dnld on 9/17/17.
14 | */
15 |
16 | public class GLideRotateTransformation extends BitmapTransformation {
17 |
18 | private float rotateRotationAngle = 0f;
19 |
20 | public GLideRotateTransformation(float rotateRotationAngle) {
21 | this.rotateRotationAngle = rotateRotationAngle;
22 | }
23 |
24 | @Override
25 | protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
26 | Matrix matrix = new Matrix();
27 |
28 | matrix.postRotate(rotateRotationAngle);
29 |
30 | return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
31 | }
32 |
33 | @Override
34 | public void updateDiskCacheKey(MessageDigest messageDigest) {
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/LegacyCompatFileProvider.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.net.Uri;
6 | import android.support.v4.content.FileProvider;
7 |
8 | import com.commonsware.cwac.provider.LegacyCompatCursorWrapper;
9 |
10 | import java.io.File;
11 |
12 | /**
13 | * Created by dnld on 16/10/17.
14 | */
15 |
16 | public class LegacyCompatFileProvider extends FileProvider {
17 |
18 | @Override
19 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
20 | return(new LegacyCompatCursorWrapper(super.query(uri, projection, selection, selectionArgs, sortOrder)));
21 | }
22 |
23 | public static Uri getUri(Context context, File file) {
24 | return getUriForFile(context, ApplicationUtils.getPackageName() + ".provider", file);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/Measure.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.graphics.Point;
6 | import android.util.DisplayMetrics;
7 | import android.view.Display;
8 | import android.view.WindowManager;
9 |
10 | /**
11 | * Created by dnld on 11/03/16.
12 | */
13 | public class Measure {
14 |
15 | public static final String TAG = "Measure";
16 |
17 | public static int pxToDp(int px, Context c) {
18 | DisplayMetrics displayMetrics = c.getResources().getDisplayMetrics();
19 | return Math.round(px * (displayMetrics.ydpi / DisplayMetrics.DENSITY_DEFAULT));
20 | }
21 | public static float dpToPx(int dp, Context context) {
22 | return dp * (context.getResources().getDisplayMetrics().density);
23 | }
24 |
25 | public static int getStatusBarHeight(Resources r) {
26 | int resourceId = r.getIdentifier("status_bar_height", "dimen", "android");
27 | if (resourceId > 0)
28 | return r.getDimensionPixelSize(resourceId);
29 |
30 | return 0;
31 | }
32 |
33 | public static int getNavBarHeight(Context ct){
34 | return getNavigationBarSize(ct).y;
35 | }
36 |
37 | public static Point getNavigationBarSize(Context context) {
38 | Point appUsableSize = getAppUsableScreenSize(context);
39 | Point realScreenSize = getRealScreenSize(context);
40 |
41 | // navigation bar on the right
42 | if (appUsableSize.x < realScreenSize.x) {
43 | return new Point(realScreenSize.x - appUsableSize.x, appUsableSize.y);
44 | }
45 |
46 | // navigation bar at the bottom
47 | if (appUsableSize.y < realScreenSize.y) {
48 | return new Point(appUsableSize.x, realScreenSize.y - appUsableSize.y);
49 | }
50 |
51 | // navigation bar is not present
52 | return new Point();
53 | }
54 |
55 | private static Point getAppUsableScreenSize(Context context) {
56 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
57 | Display display = windowManager.getDefaultDisplay();
58 | Point size = new Point();
59 | display.getSize(size);
60 | return size;
61 | }
62 |
63 | private static Point getRealScreenSize(Context context) {
64 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
65 | Display display = windowManager.getDefaultDisplay();
66 | Point size = new Point();
67 | display.getRealSize(size);
68 | return size;
69 | }
70 |
71 | public static int rotateBy(int current, int degrees) {
72 | // TODO: 21/08/16 a better way should exist
73 | /*int rotation = current + degrees;
74 | if (rotation > 359) rotation -=360;
75 | if (rotation < 0) rotation +=360;*/
76 | return (current + degrees) % 360;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/MediaUtils.kt:
--------------------------------------------------------------------------------
1 | @file: JvmName("MediaUtils")
2 |
3 | package org.horaapps.leafpic.util
4 |
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.net.Uri
8 | import android.support.v4.app.FragmentManager
9 | import android.widget.Toast
10 | import org.horaapps.leafpic.R
11 | import org.horaapps.leafpic.data.Media
12 | import org.horaapps.leafpic.data.MediaHelper
13 | import org.horaapps.leafpic.progress.ProgressBottomSheet
14 | import java.util.*
15 |
16 | /**
17 | * Share the given Media with an application.
18 | */
19 | fun shareMedia(context: Context, mediaList: List) {
20 | val intent = Intent(Intent.ACTION_SEND_MULTIPLE)
21 |
22 | val types = HashMap()
23 | val files = ArrayList()
24 |
25 | for (f in mediaList) {
26 | val mimeType = MimeTypeUtils.getTypeMime(f.mimeType)
27 | var count = 0
28 | if (types.containsKey(mimeType)) {
29 | count = types[mimeType]!!
30 | }
31 | types[mimeType] = count
32 | files.add(LegacyCompatFileProvider.getUri(context, f.file))
33 | }
34 |
35 | val fileTypes = types.keys
36 | if (fileTypes.size > 1) {
37 | Toast.makeText(context, R.string.waring_share_multiple_file_types, Toast.LENGTH_SHORT).show()
38 | }
39 |
40 | val max = -1
41 | var type: String? = null
42 | for (fileType in fileTypes) {
43 | val count = types[fileType]!!
44 | if (count > max) {
45 | type = fileType
46 | }
47 | }
48 |
49 | intent.type = type!! + "/*"
50 |
51 | intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files)
52 | intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
53 | context.startActivity(Intent.createChooser(intent, context.getString(R.string.send_to)))
54 | }
55 |
56 | fun deleteMedia(context: Context, mediaList: List, fragmentManager: FragmentManager, deleteListener: ProgressBottomSheet.Listener) {
57 | val sources = ArrayList>(mediaList.size)
58 | for (media in mediaList)
59 | sources.add(MediaHelper.deleteMedia(context.applicationContext, media))
60 |
61 | val bottomSheet = ProgressBottomSheet.Builder(R.string.delete_bottom_sheet_title)
62 | .autoDismiss(false)
63 | .sources(sources)
64 | .listener(deleteListener)
65 | .build()
66 |
67 | bottomSheet.showNow(fragmentManager, null)
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/MimeTypeUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.webkit.MimeTypeMap;
4 |
5 | public class MimeTypeUtils {
6 |
7 | public static final String UNKNOWN_MIME_TYPE = "unknown/unknown";
8 |
9 | public static String getMimeType(String path) {
10 | int index;
11 | if (path == null || (index = path.lastIndexOf('.')) == -1)
12 | return UNKNOWN_MIME_TYPE;
13 |
14 | String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(path.substring(index + 1).toLowerCase());
15 | return mime != null ? mime : UNKNOWN_MIME_TYPE;
16 | }
17 |
18 | public static String getGenericMIME(String mime) {
19 | return mime.split("/")[0] + "/*";
20 | }
21 |
22 | public static String getTypeMime(String mime) {
23 | return mime.split("/")[0];
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.support.v4.app.ActivityCompat;
8 | import android.support.v4.content.ContextCompat;
9 | import android.support.v7.app.AppCompatActivity;
10 |
11 | /**
12 | * Created by dnld on 01/04/16.
13 | */
14 | public final class PermissionUtils {
15 |
16 | public static boolean checkPermissions(Context context, String... permissions) {
17 | for (String permission : permissions) {
18 | if (!checkPermission(context, permission)) {
19 | return false;
20 | }
21 | }
22 | return true;
23 | }
24 |
25 | private static boolean checkPermission(Context context, String permission) {
26 | return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
27 | }
28 |
29 | public static boolean isStoragePermissionsGranted(Context context) {
30 | return checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
31 | }
32 |
33 | public static void requestPermissions(Object o, int permissionId, String... permissions) {
34 | if (o instanceof Activity) {
35 | ActivityCompat.requestPermissions((AppCompatActivity) o, permissions, permissionId);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/RecyclerItemClickListener.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.GestureDetector;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 |
8 | /**
9 | * Created by dnld on 3/24/17.
10 | */
11 |
12 | public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
13 |
14 | public interface OnItemClickListener {
15 | void onItemClick(View view, int position);
16 | void onLongItemClick(View view, int position);
17 | }
18 |
19 | private OnItemClickListener mListener;
20 | private GestureDetector mGestureDetector;
21 | public RecyclerItemClickListener(final RecyclerView rv, OnItemClickListener listener) {
22 | mListener = listener;
23 | mGestureDetector = new GestureDetector(rv.getContext(), new GestureDetector.SimpleOnGestureListener() {
24 | @Override
25 | public boolean onSingleTapUp(MotionEvent e) {
26 | return true;
27 | }
28 | @Override
29 | public void onLongPress(MotionEvent e) {
30 | View child = rv.findChildViewUnder(e.getX(), e.getY());
31 | if (child != null && mListener != null) {
32 | mListener.onLongItemClick(child, rv.getChildAdapterPosition(child));
33 | }
34 | }
35 | });
36 | }
37 | @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
38 | View childView = view.findChildViewUnder(e.getX(), e.getY());
39 | if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
40 | mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
41 | return true;
42 | }
43 | return false;
44 | }
45 | @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
46 | @Override
47 | public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/ServerConstants.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | /**
4 | * Storage for server constants - specifically Web URLs
5 | */
6 | public class ServerConstants {
7 |
8 | // GitHub URLs
9 | public static final String GITHUB_LEAFPIC = "https://github.com/HoraApps/LeafPic";
10 | public static final String GITHUB_DONALD = "https://github.com/DNLDsht";
11 | public static final String GITHUB_CALVIN = "https://github.com/CalvinNor";
12 | public static final String GITHUB_GILBERT = "https://github.com/Mow3l";
13 |
14 | // LeafPic URLs
15 | public static final String LEAFPIC_ISSUES = "https://github.com/HoraApps/LeafPic/issues";
16 | public static final String LEAFPIC_CROWDIN = "https://crowdin.com/project/leafpic";
17 | public static final String LEAFPIC_LICENSE = "https://github.com/HoraApps/LeafPic/blob/master/LICENSE";
18 | public static final String LEAFPIC_CHANGELOG = "https://github.com/HoraApps/LeafPic/blob/dev/CHANGELOG.md";
19 |
20 |
21 | public static final String TWITTER_ABOUT_DONALD = "https://twitter.com/dnld_sht";
22 | public static final String TWITTER_ABOUT_GILBERT = "https://twitter.com/GilbertNdr";
23 | public static final String GOOGLE_ABOUT_CALVIN = "https://plus.google.com/+CalvinNoronha2394";
24 |
25 | // Email IDs
26 | public static final String MAIL_DONALD = "dnld.sht@gmail.com";
27 | public static final String MAIL_GILBERT = "jibo95@gmail.com";
28 | public static final String MAIL_CALVIN = "calvin.nrnha@gmail.com";
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/StaticMapProvider.java:
--------------------------------------------------------------------------------
1 | package org.horaapps.leafpic.util;
2 |
3 | import com.drew.lang.GeoLocation;
4 |
5 | import org.horaapps.leafpic.SecretConstants;
6 |
7 | import java.util.Locale;
8 |
9 | // needed for Local.US
10 |
11 | /**
12 | * Created by dnld on 04/09/16.
13 | */
14 |
15 | public enum StaticMapProvider {
16 |
17 | GOOGLE_MAPS(0), MAP_BOX(1), MAP_BOX_DARK(2), MAP_BOX_LIGHT(3), TYLER(4);
18 |
19 | int value;
20 |
21 | StaticMapProvider(int value) {
22 | this.value = value;
23 | }
24 |
25 | public int getValue() { return value;}
26 |
27 | /** String.format is locale dependent, we need to force a locale with point instead of comma in decimals,
28 | * otherwise (at least) mapbox does not work in some countries */
29 | public String getUrl(GeoLocation location) {
30 | if (value>=1 && value <=3) //MAP_MOX invert coordinates
31 | return String.format(Locale.US, getUrl(value), location.getLongitude(), location.getLatitude());
32 | return String.format(Locale.US, getUrl(value), location.getLatitude(), location.getLongitude());
33 | }
34 |
35 | private String getUrl(int value) {
36 | switch (value) {
37 | case 0: default: return ("http://maps.google.com/maps/api/staticmap?center=%f,%f&zoom=15&size=500x300&scale=2&sensor=false");
38 | case 1: return "https://api.mapbox.com/v4/mapbox.streets/%f,%f,15/500x300.jpg?access_token=" + SecretConstants.MAP_BOX_TOKEN;
39 | case 2: return "https://api.mapbox.com/v4/mapbox.dark/%f,%f,15/500x300.jpg?access_token="+ SecretConstants.MAP_BOX_TOKEN;
40 | case 3: return "https://api.mapbox.com/v4/mapbox.light/%f,%f,15/500x300.jpg?access_token="+ SecretConstants.MAP_BOX_TOKEN;
41 | case 4: return "https://tyler-demo.herokuapp.com/?greyscale=false&lat=%f&lon=%f&zoom=15&width=500&height=300&tile_url=http://[abcd].tile.stamen.com/watercolor/{zoom}/{x}/{y}.jpg";
42 | }
43 | }
44 |
45 | public static StaticMapProvider fromValue(int value) {
46 | switch (value) {
47 | case 0: default: return GOOGLE_MAPS;
48 | case 1: return MAP_BOX;
49 | case 2: return MAP_BOX_DARK;
50 | case 3: return MAP_BOX_LIGHT;
51 | case 4: return TYLER;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/org/horaapps/leafpic/util/inapppurchase/IabBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2014 Google Inc.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package org.horaapps.leafpic.util.inapppurchase;
17 |
18 | import android.content.BroadcastReceiver;
19 | import android.content.Context;
20 | import android.content.Intent;
21 |
22 | /**
23 | * Receiver for the "com.android.vending.billing.PURCHASES_UPDATED" Action
24 | * from the Play Store.
25 | *
26 | *
It is possible that an in-app item may be acquired without the
27 | * application calling getBuyIntent(), for example if the item can be
28 | * redeemed from inside the Play Store using a promotional code. If this
29 | * application isn't running at the time, then when it is started a call
30 | * to getPurchases() will be sufficient notification. However, if the
31 | * application is already running in the background when the item is acquired,
32 | * a message to this BroadcastReceiver will indicate that the an item
33 | * has been acquired.