[JAVA] 2billion cats, AndroidN EasterEgg, Make a lot of cats ~ HyperUltraCatGenerator ~

HyperUltraCatGenerator! It is an application that makes a lot of cats of Android N's Easter egg, Neko Atsume.

ezgif-3-747df78d5897.gif

generate cat You can generate any Android Cat by turning the Number Picker on the left or specifying it on the right. It will be shared by tapping.

save 1000 cats You can generate an Android Cat with the name in the range specified by from ~ to. The generated Android Cat is saved in Pictures / Cats.

Repository

https://github.com/nekomimi-daimao/hyper-ultra-cat-generator

Code description

Follow the path of creating this fucking app while doing a self-code review of each commit. I think it would be fun to keep track of what people are thinking about when developing. If you are free to go out with me, please read while looking at the repository.

initial commit The image I use for the icon is generated from Android N's Easter Egg and Neko Atsume. The impression that the images I collected are exposed to each other on the net and that they are relatively popular. There are also some clone games on the playStore. Android Nougat Easter Egg Neko Easter Egg I thought I was playing a clone game, but I can't mass-produce Android Cat. Place the food and wait, or tap it to generate it one by one. I thought. ** I want a lot. ** **

import cats First, create an activity. I'm always Empty Activity because it's rare that it doesn't compile with automatic generation. https://android.googlesource.com/platform/frameworks/base/+/nougat-mr2.3-release/packages/EasterEgg Extract the necessary files from the repository like that and rewrite the parts that do not compile. Cat.java. Anyway, when I read the code, Cat seems to be generated in the following places. Basically, generate it randomly with Cat.create (), and if you want an arbitrary Android Cat, pass the seed of int to the constructor of Cat .... I don't know what the dyeing algorithm is, so I wonder if it's okay ...

Cat.java



//Random generation is this
public static Cat create(Context context) {
    return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt()));
}

//The generation logic is the constructor
public Cat(Context context, long seed) {
    D = new CatParts(context);
    mSeed = seed;
    setName(context.getString(R.string.default_cat_name,
            String.valueOf(mSeed % 1000)));
    final Random nsr = notSoRandom(seed);
    // body color
    mBodyColor = chooseP(nsr, P_BODY_COLORS);
    if (mBodyColor == 0) mBodyColor = Color.HSVToColor(new float[] {
            nsr.nextFloat()*360f, frandrange(nsr,0.5f,1f), frandrange(nsr,0.5f, 1f)});
    tint(mBodyColor, D.body, D.head, D.leg1, D.leg2, D.leg3, D.leg4, D.tail,
            D.leftEar, D.rightEar, D.foot1, D.foot2, D.foot3, D.foot4, D.tailCap);
    tint(0x20000000, D.leg2Shadow, D.tailShadow);
    if (isDark(mBodyColor)) {
        tint(0xFFFFFFFF, D.leftEye, D.rightEye, D.mouth, D.nose);
    }
    tint(isDark(mBodyColor) ? 0xFFEF9A9A : 0x20D50000, D.leftEarInside, D.rightEarInside);
    tint(chooseP(nsr, P_BELLY_COLORS), D.belly);
    tint(chooseP(nsr, P_BELLY_COLORS), D.back);
    final int faceColor = chooseP(nsr, P_BELLY_COLORS);
    tint(faceColor, D.faceSpot);
    if (!isDark(faceColor)) {
        tint(0xFF000000, D.mouth, D.nose);
    }
    mFootType = 0;
    if (nsr.nextFloat() < 0.25f) {
        mFootType = 4;
        tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4);
    } else {
        if (nsr.nextFloat() < 0.25f) {
            mFootType = 2;
            tint(0xFFFFFFFF, D.foot1, D.foot3);
        } else if (nsr.nextFloat() < 0.25f) {
            mFootType = 3; // maybe -2 would be better? meh.
            tint(0xFFFFFFFF, D.foot2, D.foot4);
        } else if (nsr.nextFloat() < 0.1f) {
            mFootType = 1;
            tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4));
        }
    }
    tint(nsr.nextFloat() < 0.333f ? 0xFFFFFFFF : mBodyColor, D.tailCap);
    final int capColor = chooseP(nsr, isDark(mBodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS);
    tint(capColor, D.cap);
    //tint(chooseP(nsr, isDark(bodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS), D.nose);
    final int collarColor = chooseP(nsr, P_COLLAR_COLORS);
    tint(collarColor, D.collar);
    mBowTie = nsr.nextFloat() < 0.1f;
    tint(mBowTie ? collarColor : 0, D.bowtie);
}

cat is a seekbar

How to use seek bar / GETTING STARTED I haven't used it for the time being, so I tried using the seek bar. I adopted Android Cat because it looked interesting when I set Android Cat dynamically on the seek bar knob. Align the track and knob of Android SeekBar For some reason, the position of Android Cat is fixed, but it's still the first time and I thought I should fix it later, so I left it. The seek bar range is 0 to 999. Yes, I was still thinking at this time. "I guess from the name of Android Cat, the total number is 1000 from # 0 to # 999" ...

cat is a picker Enter numbers with NUMBER PICKER / GETTING STARTED Another one, Number Picker, which I've always wanted to use. If you turn it around, Android Cat will be generated round and round, and it's just fun. Set the maximum value to ʻInteger.MAX_VALUE`, and if you turn it further, it should be 0, but it overflows and the next number is not displayed. It seems to be negative. I think it's a bug, but I can't help it because it's not supposed to be used in such an unreasonable way.

Since it is difficult to turn around to the desired number, a jump function has been added. But. mPicker.setValue (jump); does not come with ʻonProgressChanged`. When I searched for it, there was a person who had the same question.

https://stackoverrun.com/ja/q/10095347

I can't help it, so read NumberPicker.java. I was surprised to inherit LiearLayout, but I am convinced.

NumberPicker.java


    /*
     * @param value The current value.
     * @see #setWrapSelectorWheel(boolean)
     * @see #setMinValue(int)
     * @see #setMaxValue(int)
     */
    public void setValue(int value) {
        setValueInternal(value, false);
    }

This is the one that can be kicked from the outside. And what this is kicking is below.

NumberPicker.java


   /**
     * Sets the current value of this NumberPicker.
     *
     * @param current The new value of the NumberPicker.
     * @param notifyChange Whether to notify if the current value changed.
     */
    private void setValueInternal(int current, boolean notifyChange) {
        if (mValue == current) {
            return;
        }
        // Wrap around the values if we go past the start or end
        if (mWrapSelectorWheel) {
            current = getWrappedSelectorIndex(current);
        } else {
            current = Math.max(current, mMinValue);
            current = Math.min(current, mMaxValue);
        }
        int previous = mValue;
        mValue = current;
        // If we're flinging, we'll update the text view at the end when it becomes visible
        if (mScrollState != OnScrollListener.SCROLL_STATE_FLING) {
            updateInputTextView();
        }
        if (notifyChange) {
            notifyChange(previous, current);
        }
        initializeSelectorWheelIndices();
        invalidate();
    }

** Why are you private? ** ** Since the value is set from the outside, it is decided that the callback of the value change is also expected, or it seems that it is okay to let me choose whether to notify the callback of the change notification. Since it can't be helped, I'll make it look like the callback was executed when the value was set.

cat is cardview, cat is extracter, cat is start There are no major changes, so I will summarize them. Cat is card view doesn't compile because it seems that I have committed something properly. Anyway, it was hard to see Android Cat on a white background, so I introduced CardView. Originally, I should make a layout with ConstraintLayout and CoordinatorLayout, but I haven't studied at all yet, so I just got used to RelativeLayout. In the layout editor, it is classified as Legacy. Since the error display is annoying, I went out to string.xml. Click on the light bulb or hit ʻalt + Enter` and Android Studio will do the rest. After that, if you make it possible to share the generated Android Cat by inserting the initial value generated appropriately at the time of initial display of the page, the generate cat part will end.

cat is provider But, however. When I ran shareCat, which I had inadvertently committed with cat is a picker, it crashed brilliantly. Why is it crashing! ** Even though it's a copy! ** **

E/AndroidRuntime: FATAL EXCEPTION: main


    Process: plan.militarize.stray.cat.hyperultracatgenerator, PID: 5979
    android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/Cats/Cat_180.png exposed beyond app through ClipData.Item.getUri()

[Android]android.os.FileUriExposedException FileUriExposedException occurs on Android 7.0 Nougat File sharing behavior by file: // schema on Android N

Search by error message. It seems that it was not given WRITE_EXTERNAL_STORAGE, but it seems that the handling of Uri has become stricter from Android N. Is it even more explicit if you go out with files? I haven't done much about ContetProvider, so it's about an hour. I haven't fully understood it, but I went to the point where it works for the time being. It's a mystery that the original NekoLand.java doesn't crash, but maybe because Easter Egg is a privileged app stored in the system area?

cat is snack It's like Toast! That's why we introduced a snack bar that we hear is the latest trend. Displaying the Snackbar I heard that it appears under the view that was used as an argument, but it appears at the bottom of the screen. I thought it was okay to leave it alone. Add a jump button to Pictures where Android Cat will be saved. I don't really think about what to do with flying **. In addition, I will create a dedicated display place for Android Cat, which was the knob of the seek bar. Set the version of Support Library to constant with gradle. I always think that it's annoying to separate the version or another file ... Isn't it annoying? It is certain that gradle tends to be chaotic.

cat is permission RuntimePermission is supported here. Java 8 is also introduced for some reason. Lambda is good!

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    for (int grantResult : grantResults) {
        if (grantResult != PackageManager.PERMISSION_GRANTED) {
            Snackbar.make(mCardSave, R.string.request_permission, Snackbar.LENGTH_SHORT).show();
            return;
        }
    }
    switch (requestCode) {
        case R.id.current_cat:
            shareCat(new Cat(getApplicationContext(), mPicker.getValue()));
            break;
        case R.id.save_cats:
            // TODO save tha cats
            break;
        default:
            return;
    }
}

RuntimePermission callback management is annoying because you have to remember which function requested which Permission. Since both requestCode and viewId are int, they are included as they are. It's just a matter of defining the requestCode yourself, but then it's okay to leave it as it is ... Why did you add READ_EXTERNAL_STORAGE? Probably not needed.

cat is foreground Start implementing the save 1000 cats part.

Neko Easter Egg

This can't really be compared to the easter egg, as this just ruins all of the fun and gives you over 4 billion different cat possibilities...but perhaps you'll find the fun in looking at them one at a time. ;)

4 billion different cat possibilities... I'll keep it ...?

Cat.java


public static Cat create(Context context) {
    return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt()));
}

It is this ThreadLocalRandom.current (). NextInt () that determines the Android Cat. ThreadLocalRandom#nextInt I haven't written it, but it probably can return all int values. The range of int is -2147483648 ~ 2147483647, and since it is an absolute value with Math.abs (), there should be 2147483647 Android Cats. If you include 0, it may be 2147483648, but it is annoying and does not calculate. I'll keep it ...

I am stunned to implement the ForegroundService of IntentService. I think it's a fixed phrase, so it's appropriate. Sometimes I forget the permission FOREGROUND_SERVICE and it makes me feel painful.

cat is progress Android Progress Notification with Examples I haven't done the progress of Notification, so I created it.

If you think that progress is not updated once in a while, it seems that there is a limit to the frequency of Notification updates. Android Nougat and rate limiting of notification updates Aside from such a fucking app, is there an ordinary app that is updated so often? No user wants to know the progress in such detail.

For the time being, the Android Cat generation logic is as follows.

CatServeService.java


Set<Integer> set = new HashSet<>(MAX);
List<Future<?>> list = new ArrayList<Future<?>>(MAX);
executorService = Executors.newWorkStealingPool();

while (set.size() < MAX) {
    int seed = Math.abs(ThreadLocalRandom.current().nextInt());
    int name = seed % 1000;
    if (name < from || to < name) {
        continue;
    }
    if (set.contains(name)) {
        continue;
    }
    set.add(name);
    list.add(executorService.submit(new CatSaver(seed)));
}
executorService.shutdown();

I'm proud that it's a ** macho logic ** that will die if you keep hitting it with a dumbbell. If the random number generation is biased, you will not be able to get out of the while loop forever, but as long as you try it with the emulator, it will always get out immediately, so it will be okay. Perhaps. CatSaver is a ridiculous Runnable that just copies the contents of shareCat. Pass it to ExecutorService and wait.

CatServeService.java


for (int count = 0; count < list.size(); count++) {
    try {
        list.get(count).get();
        builder.setProgress(MAX, count, false);
        manager.notify(NOTIFICATION_ID_SAVE_CAT_PROGRESS, builder.build());
    } catch (ExecutionException | InterruptedException e) {
    }
}

Originally, it should be updated as soon as each person completes, not in the order passed to ExecutorService, or retry of the failed task should be included, but I stopped it because it is troublesome. Multithreaded scary.

cat is name I had a name.

cat is crash Up to this point, we have confirmed the operation with the P emulator. Here, when I put it in the actual machine of N and generated a large amount of Android Cat ** crash **. ** Crash ** when I make an emulator for N and run it. In particular, logcat does not dye red </ font> ... It's heavy and I don't like it very much, but when I start Profiler, the memory increases a lot and it crashes. ** It's an image, so it's a memory leak! ** ** So measures.

  • Make the inner class static
  • Bitmap.recycle()
  • Put null in the used Bitmap and Drawable

It still crashed. Since it can't be helped, I changed the place where Android Cat was generated by multithread to single thread and it no longer crashes.

CatServeService.java


//before multithread
executorService = Executors.newWorkStealingPool();

//after single thread
executorService = Executors.newSingleThreadExecutor();

The Executor Service that can be easily switched is God. It seems that IO with multithreading is also awkward, but I feel that this crash is something wrong with the memory release processing of N Bitmap and Drawable. If it is O or P, the GC will run around and will not crash.

cat is icon How to support Adaptive Icon for the time being Create Adaptive Icon in Android Studio Since it's a big deal, I'll try to make an Adaptive Icon. It seems that the number of compatible home apps is small as of October 16, 2018, and its power could not be confirmed. I feel that there is no other way to use it than to instigate it by saying, "Ah, this app is compatible with Adaptive Icon! If you don't catch up with the new specifications of the framework, it's pretty cruel selection that users will see the fucking icon. By the way, I made this fucking icon with gimp for 20 minutes, but when I think about it now, it might have been cooler to carve the Android Cat vertically instead of horizontally.

cat is font Kielo Typeface Fonts are also added. I also added a toolbar. I think it's best to leave ordinary fonts to terminal standard fonts, but it may be fun to use decorative fonts as such accents. At first, I thought I would use this, but * I don't need two kinds of cats *, so it feels fashionable.

cat is large icon Large Icon is not displayed in Notification after O. I haven't verified it well, but it seems that Adaptive Icon cannot be set to Large Icon. Bitmap may not be generated. If it's a Vector, you can go ...? It's annoying, so ** copy the icon image automatically generated from the mipmap folder and insert it into the drawable folder **. Power is power!

cat is modifications Various adjustments. I noticed that ** notification sound keeps ringing ** every time progress is updated, so I changed from NotificationManager.IMPORTANCE_DEFAULT to NotificationManager.IMPORTANCE_LOW. You're doing .setCategory (NotificationCompat.CATEGORY_PROGRESS)! Don't ring! I think, but well, it can't be helped ...

fix foregroudService as it could also crash. A Service started with ContextCompat.startForegroundService will crash if there is onlystartForeground ()even if the Intent is flawed and it ends without doing anything. It's an error case that's hard to think of, so it's hard to discover it later.

The rest is Media Scanner Connection. I do this when I want other apps to see the generated images immediately, but I do not like the fact that I am constantly requesting each image, so I asked them to do it all at once. did. I did this really somehow, so if you have any knowledge, please let me know.

in conclusion

Now you can freely generate a large number of Android Cats. As I write this article, just before this publication, I think I'm the person who produced the most Android Cats on this planet **. Please use HyperUltraCat Generator to generate a large number of Android Cats and use them for icons and so on!

bonus

There was [a person who is really analyzing](https://zau2and.blogspot.com/search/label/ Neko Atsume). To be clear, if you read this article at the time you read this article, you will get better from Android. I found it after making the app. It's true.

Bonus bonus

The average image size of Android Cat is 14kb. 14 * 2147483647 = 30064771058

f2668204-9e05-4e18-b941-82f629d57bd0.png

It's amazing.

Recommended Posts

2billion cats, AndroidN EasterEgg, Make a lot of cats ~ HyperUltraCatGenerator ~
Make a note of Ruby keyword arguments
Make a margin to the left of the TextField