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