OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / packages / EasterEgg / src / com / android / egg / neko / NekoLand.java
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 }