Home | History | Annotate | Download | only in neko
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
      5  * except in compliance with the License. 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 distributed under the
     10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     11  * KIND, either express or implied. See the License for the specific language governing
     12  * permissions and limitations under the License.
     13  */
     14 
     15 package com.android.egg.neko;
     16 
     17 import android.Manifest;
     18 import android.app.ActionBar;
     19 import android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.DialogInterface.OnClickListener;
     24 import android.content.Intent;
     25 import android.content.pm.PackageManager;
     26 import android.graphics.Bitmap;
     27 import android.graphics.Color;
     28 import android.graphics.drawable.Drawable;
     29 import android.media.MediaScannerConnection;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.os.Environment;
     33 import android.provider.MediaStore.Images;
     34 import android.support.v4.content.FileProvider;
     35 import android.support.v7.widget.GridLayoutManager;
     36 import android.support.v7.widget.RecyclerView;
     37 import android.util.Log;
     38 import android.view.ContextThemeWrapper;
     39 import android.view.LayoutInflater;
     40 import android.view.View;
     41 import android.view.View.OnLongClickListener;
     42 import android.view.ViewGroup;
     43 import android.widget.EditText;
     44 import android.widget.ImageView;
     45 import android.widget.TextView;
     46 
     47 import com.android.egg.R;
     48 import com.android.egg.neko.PrefState.PrefsListener;
     49 import com.android.internal.logging.MetricsLogger;
     50 
     51 import java.io.File;
     52 import java.io.FileOutputStream;
     53 import java.io.IOException;
     54 import java.io.OutputStream;
     55 import java.util.Collections;
     56 import java.util.Comparator;
     57 import java.util.List;
     58 
     59 public class NekoLand extends Activity implements PrefsListener {
     60     public static String CHAN_ID = "EGG";
     61 
     62     public static boolean DEBUG = false;
     63     public static boolean DEBUG_NOTIFICATIONS = false;
     64 
     65     private static final int EXPORT_BITMAP_SIZE = 600;
     66 
     67     private static final int STORAGE_PERM_REQUEST = 123;
     68 
     69     private static boolean CAT_GEN = false;
     70     private PrefState mPrefs;
     71     private CatAdapter mAdapter;
     72     private Cat mPendingShareCat;
     73 
     74 
     75     @Override
     76     public void onCreate(Bundle savedInstanceState) {
     77         super.onCreate(savedInstanceState);
     78         setContentView(R.layout.neko_activity);
     79         final ActionBar actionBar = getActionBar();
     80         if (actionBar != null) {
     81             actionBar.setLogo(Cat.create(this));
     82             actionBar.setDisplayUseLogoEnabled(false);
     83             actionBar.setDisplayShowHomeEnabled(true);
     84         }
     85 
     86         mPrefs = new PrefState(this);
     87         mPrefs.setListener(this);
     88         final RecyclerView recyclerView = findViewById(R.id.holder);
     89         mAdapter = new CatAdapter();
     90         recyclerView.setAdapter(mAdapter);
     91         recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
     92         int numCats = updateCats();
     93         MetricsLogger.histogram(this, "egg_neko_visit_gallery", numCats);
     94     }
     95 
     96     @Override
     97     protected void onDestroy() {
     98         super.onDestroy();
     99         mPrefs.setListener(null);
    100     }
    101 
    102     private int updateCats() {
    103         Cat[] cats;
    104         if (CAT_GEN) {
    105             cats = new Cat[50];
    106             for (int i = 0; i < cats.length; i++) {
    107                 cats[i] = Cat.create(this);
    108             }
    109         } else {
    110             final float[] hsv = new float[3];
    111             List<Cat> list = mPrefs.getCats();
    112             Collections.sort(list, new Comparator<Cat>() {
    113                 @Override
    114                 public int compare(Cat cat, Cat cat2) {
    115                     Color.colorToHSV(cat.getBodyColor(), hsv);
    116                     float bodyH1 = hsv[0];
    117                     Color.colorToHSV(cat2.getBodyColor(), hsv);
    118                     float bodyH2 = hsv[0];
    119                     return Float.compare(bodyH1, bodyH2);
    120                 }
    121             });
    122             cats = list.toArray(new Cat[0]);
    123         }
    124         mAdapter.setCats(cats);
    125         return cats.length;
    126     }
    127 
    128     private void onCatClick(Cat cat) {
    129         if (CAT_GEN) {
    130             mPrefs.addCat(cat);
    131             new AlertDialog.Builder(NekoLand.this)
    132                     .setTitle("Cat added")
    133                     .setPositiveButton(android.R.string.ok, null)
    134                     .show();
    135         } else {
    136             showNameDialog(cat);
    137         }
    138 //      noman.notify(1, cat.buildNotification(NekoLand.this).build());
    139     }
    140 
    141     private void onCatRemove(Cat cat) {
    142         cat.logRemove(this);
    143         mPrefs.removeCat(cat);
    144     }
    145 
    146     private void showNameDialog(final Cat cat) {
    147         final Context context = new ContextThemeWrapper(this,
    148                 android.R.style.Theme_Material_Light_Dialog_NoActionBar);
    149         // TODO: Move to XML, add correct margins.
    150         View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
    151         final EditText text = (EditText) view.findViewById(android.R.id.edit);
    152         text.setText(cat.getName());
    153         text.setSelection(cat.getName().length());
    154         final int size = context.getResources()
    155                 .getDimensionPixelSize(android.R.dimen.app_icon_size);
    156         Drawable catIcon = cat.createIcon(this, size, size).loadDrawable(this);
    157         new AlertDialog.Builder(context)
    158                 .setTitle(" ")
    159                 .setIcon(catIcon)
    160                 .setView(view)
    161                 .setPositiveButton(android.R.string.ok, new OnClickListener() {
    162                     @Override
    163                     public void onClick(DialogInterface dialog, int which) {
    164                         cat.logRename(context);
    165                         cat.setName(text.getText().toString().trim());
    166                         mPrefs.addCat(cat);
    167                     }
    168                 }).show();
    169     }
    170 
    171     @Override
    172     public void onPrefsChanged() {
    173         updateCats();
    174     }
    175 
    176     private class CatAdapter extends RecyclerView.Adapter<CatHolder> {
    177 
    178         private Cat[] mCats;
    179 
    180         public void setCats(Cat[] cats) {
    181             mCats = cats;
    182             notifyDataSetChanged();
    183         }
    184 
    185         @Override
    186         public CatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    187             return new CatHolder(LayoutInflater.from(parent.getContext())
    188                     .inflate(R.layout.cat_view, parent, false));
    189         }
    190 
    191         private void setContextGroupVisible(final CatHolder holder, boolean vis) {
    192             final View group = holder.contextGroup;
    193             if (vis && group.getVisibility() != View.VISIBLE) {
    194                 group.setAlpha(0);
    195                 group.setVisibility(View.VISIBLE);
    196                 group.animate().alpha(1.0f).setDuration(333);
    197                 Runnable hideAction = new Runnable() {
    198                     @Override
    199                     public void run() {
    200                         setContextGroupVisible(holder, false);
    201                     }
    202                 };
    203                 group.setTag(hideAction);
    204                 group.postDelayed(hideAction, 5000);
    205             } else if (!vis && group.getVisibility() == View.VISIBLE) {
    206                 group.removeCallbacks((Runnable) group.getTag());
    207                 group.animate().alpha(0f).setDuration(250).withEndAction(new Runnable() {
    208                     @Override
    209                     public void run() {
    210                         group.setVisibility(View.INVISIBLE);
    211                     }
    212                 });
    213             }
    214         }
    215 
    216         @Override
    217         public void onBindViewHolder(final CatHolder holder, int position) {
    218             Context context = holder.itemView.getContext();
    219             final int size = context.getResources().getDimensionPixelSize(R.dimen.neko_display_size);
    220             holder.imageView.setImageIcon(mCats[position].createIcon(context, size, size));
    221             holder.textView.setText(mCats[position].getName());
    222             holder.itemView.setOnClickListener(new View.OnClickListener() {
    223                 @Override
    224                 public void onClick(View v) {
    225                     onCatClick(mCats[holder.getAdapterPosition()]);
    226                 }
    227             });
    228             holder.itemView.setOnLongClickListener(new OnLongClickListener() {
    229                 @Override
    230                 public boolean onLongClick(View v) {
    231                     setContextGroupVisible(holder, true);
    232                     return true;
    233                 }
    234             });
    235             holder.delete.setOnClickListener(new View.OnClickListener() {
    236                 @Override
    237                 public void onClick(View v) {
    238                     setContextGroupVisible(holder, false);
    239                     new AlertDialog.Builder(NekoLand.this)
    240                         .setTitle(getString(R.string.confirm_delete, mCats[position].getName()))
    241                         .setNegativeButton(android.R.string.cancel, null)
    242                         .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    243                             @Override
    244                             public void onClick(DialogInterface dialog, int which) {
    245                                 onCatRemove(mCats[holder.getAdapterPosition()]);
    246                             }
    247                         })
    248                         .show();
    249                 }
    250             });
    251             holder.share.setOnClickListener(new View.OnClickListener() {
    252                 @Override
    253                 public void onClick(View v) {
    254                     setContextGroupVisible(holder, false);
    255                     Cat cat = mCats[holder.getAdapterPosition()];
    256                     if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    257                             != PackageManager.PERMISSION_GRANTED) {
    258                         mPendingShareCat = cat;
    259                         requestPermissions(
    260                                 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
    261                                 STORAGE_PERM_REQUEST);
    262                         return;
    263                     }
    264                     shareCat(cat);
    265                 }
    266             });
    267         }
    268 
    269         @Override
    270         public int getItemCount() {
    271             return mCats.length;
    272         }
    273     }
    274 
    275     private void shareCat(Cat cat) {
    276         final File dir = new File(
    277                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
    278                 getString(R.string.directory_name));
    279         if (!dir.exists() && !dir.mkdirs()) {
    280             Log.e("NekoLand", "save: error: can't create Pictures directory");
    281             return;
    282         }
    283         final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png");
    284         Bitmap bitmap = cat.createBitmap(EXPORT_BITMAP_SIZE, EXPORT_BITMAP_SIZE);
    285         if (bitmap != null) {
    286             try {
    287                 OutputStream os = new FileOutputStream(png);
    288                 bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
    289                 os.close();
    290                 MediaScannerConnection.scanFile(
    291                         this,
    292                         new String[] {png.toString()},
    293                         new String[] {"image/png"},
    294                         null);
    295                 Log.v("Neko", "cat file: " + png);
    296                 Uri uri = FileProvider.getUriForFile(this, "com.android.egg.fileprovider", png);
    297                 Log.v("Neko", "cat uri: " + uri);
    298                 Intent intent = new Intent(Intent.ACTION_SEND);
    299                 intent.putExtra(Intent.EXTRA_STREAM, uri);
    300                 intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName());
    301                 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    302                 intent.setType("image/png");
    303                 startActivity(Intent.createChooser(intent, null));
    304                 cat.logShare(this);
    305             } catch (IOException e) {
    306                 Log.e("NekoLand", "save: error: " + e);
    307             }
    308         }
    309     }
    310 
    311     @Override
    312     public void onRequestPermissionsResult(int requestCode,
    313                                            String permissions[], int[] grantResults) {
    314         if (requestCode == STORAGE_PERM_REQUEST) {
    315             if (mPendingShareCat != null) {
    316                 shareCat(mPendingShareCat);
    317                 mPendingShareCat = null;
    318             }
    319         }
    320     }
    321 
    322     private static class CatHolder extends RecyclerView.ViewHolder {
    323         private final ImageView imageView;
    324         private final TextView textView;
    325         private final View contextGroup;
    326         private final View delete;
    327         private final View share;
    328 
    329         public CatHolder(View itemView) {
    330             super(itemView);
    331             imageView = (ImageView) itemView.findViewById(android.R.id.icon);
    332             textView = (TextView) itemView.findViewById(android.R.id.title);
    333             contextGroup = itemView.findViewById(R.id.contextGroup);
    334             delete = itemView.findViewById(android.R.id.closeButton);
    335             share = itemView.findViewById(android.R.id.shareText);
    336         }
    337     }
    338 }
    339