• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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
onDestroy()97     protected void onDestroy() {
98         super.onDestroy();
99         mPrefs.setListener(null);
100     }
101 
updateCats()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 
onCatClick(Cat cat)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 
onCatRemove(Cat cat)141     private void onCatRemove(Cat cat) {
142         cat.logRemove(this);
143         mPrefs.removeCat(cat);
144     }
145 
showNameDialog(final Cat cat)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
onPrefsChanged()172     public void onPrefsChanged() {
173         updateCats();
174     }
175 
176     private class CatAdapter extends RecyclerView.Adapter<CatHolder> {
177 
178         private Cat[] mCats;
179 
setCats(Cat[] cats)180         public void setCats(Cat[] cats) {
181             mCats = cats;
182             notifyDataSetChanged();
183         }
184 
185         @Override
onCreateViewHolder(ViewGroup parent, int viewType)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 
setContextGroupVisible(final CatHolder holder, boolean vis)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
onBindViewHolder(final CatHolder holder, int position)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
getItemCount()270         public int getItemCount() {
271             return mCats.length;
272         }
273     }
274 
shareCat(Cat cat)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
onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)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 
CatHolder(View itemView)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