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 15package com.android.egg.neko; 16 17import android.Manifest; 18import android.app.ActionBar; 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.content.Context; 22import android.content.DialogInterface; 23import android.content.DialogInterface.OnClickListener; 24import android.content.Intent; 25import android.content.pm.PackageManager; 26import android.graphics.Bitmap; 27import android.graphics.drawable.Drawable; 28import android.media.MediaScannerConnection; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.Environment; 32import android.provider.MediaStore.Images; 33import android.support.v7.widget.GridLayoutManager; 34import android.support.v7.widget.RecyclerView; 35import android.util.Log; 36import android.view.ContextThemeWrapper; 37import android.view.LayoutInflater; 38import android.view.View; 39import android.view.View.OnLongClickListener; 40import android.view.ViewGroup; 41import android.widget.EditText; 42import android.widget.ImageView; 43import android.widget.TextView; 44 45import com.android.egg.R; 46import com.android.egg.neko.PrefState.PrefsListener; 47 48import java.io.File; 49import java.io.FileOutputStream; 50import java.io.IOException; 51import java.io.OutputStream; 52 53public class NekoLand extends Activity implements PrefsListener { 54 public static boolean DEBUG = false; 55 public static boolean DEBUG_NOTIFICATIONS = false; 56 57 private static final int STORAGE_PERM_REQUEST = 123; 58 59 private static boolean CAT_GEN = false; 60 private PrefState mPrefs; 61 private CatAdapter mAdapter; 62 private Cat mPendingShareCat; 63 64 65 @Override 66 public void onCreate(Bundle savedInstanceState) { 67 super.onCreate(savedInstanceState); 68 setContentView(R.layout.neko_activity); 69 final ActionBar actionBar = getActionBar(); 70 if (actionBar != null) { 71 actionBar.setLogo(Cat.create(this)); 72 actionBar.setDisplayUseLogoEnabled(false); 73 actionBar.setDisplayShowHomeEnabled(true); 74 } 75 76 mPrefs = new PrefState(this); 77 mPrefs.setListener(this); 78 final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.holder); 79 mAdapter = new CatAdapter(); 80 recyclerView.setAdapter(mAdapter); 81 recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); 82 updateCats(); 83 } 84 85 @Override 86 protected void onDestroy() { 87 super.onDestroy(); 88 mPrefs.setListener(null); 89 } 90 91 private void updateCats() { 92 Cat[] cats; 93 if (CAT_GEN) { 94 cats = new Cat[50]; 95 for (int i = 0; i < cats.length; i++) { 96 cats[i] = Cat.create(this); 97 } 98 } else { 99 cats = mPrefs.getCats().toArray(new Cat[0]); 100 } 101 mAdapter.setCats(cats); 102 } 103 104 private void onCatClick(Cat cat) { 105 if (CAT_GEN) { 106 mPrefs.addCat(cat); 107 new AlertDialog.Builder(NekoLand.this) 108 .setTitle("Cat added") 109 .setPositiveButton(android.R.string.ok, null) 110 .show(); 111 } else { 112 showNameDialog(cat); 113 } 114// noman.notify(1, cat.buildNotification(NekoLand.this).build()); 115 } 116 117 private void onCatRemove(Cat cat) { 118 mPrefs.removeCat(cat); 119 } 120 121 private void showNameDialog(final Cat cat) { 122 Context context = new ContextThemeWrapper(this, 123 android.R.style.Theme_Material_Light_Dialog_NoActionBar); 124 // TODO: Move to XML, add correct margins. 125 View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null); 126 final EditText text = (EditText) view.findViewById(android.R.id.edit); 127 text.setText(cat.getName()); 128 text.setSelection(cat.getName().length()); 129 Drawable catIcon = cat.createLargeIcon(this).loadDrawable(this); 130 new AlertDialog.Builder(context) 131 .setTitle(" ") 132 .setIcon(catIcon) 133 .setView(view) 134 .setPositiveButton(android.R.string.ok, new OnClickListener() { 135 @Override 136 public void onClick(DialogInterface dialog, int which) { 137 cat.setName(text.getText().toString().trim()); 138 mPrefs.addCat(cat); 139 } 140 }).show(); 141 } 142 143 @Override 144 public void onPrefsChanged() { 145 updateCats(); 146 } 147 148 private class CatAdapter extends RecyclerView.Adapter<CatHolder> { 149 150 private Cat[] mCats; 151 152 public void setCats(Cat[] cats) { 153 mCats = cats; 154 notifyDataSetChanged(); 155 } 156 157 @Override 158 public CatHolder onCreateViewHolder(ViewGroup parent, int viewType) { 159 return new CatHolder(LayoutInflater.from(parent.getContext()) 160 .inflate(R.layout.cat_view, parent, false)); 161 } 162 163 @Override 164 public void onBindViewHolder(final CatHolder holder, int position) { 165 Context context = holder.itemView.getContext(); 166 holder.imageView.setImageIcon(mCats[position].createLargeIcon(context)); 167 holder.textView.setText(mCats[position].getName()); 168 holder.itemView.setOnClickListener(new View.OnClickListener() { 169 @Override 170 public void onClick(View v) { 171 onCatClick(mCats[holder.getAdapterPosition()]); 172 } 173 }); 174 holder.itemView.setOnLongClickListener(new OnLongClickListener() { 175 @Override 176 public boolean onLongClick(View v) { 177 holder.contextGroup.removeCallbacks((Runnable) holder.contextGroup.getTag()); 178 holder.contextGroup.setVisibility(View.VISIBLE); 179 Runnable hideAction = new Runnable() { 180 @Override 181 public void run() { 182 holder.contextGroup.setVisibility(View.INVISIBLE); 183 } 184 }; 185 holder.contextGroup.setTag(hideAction); 186 holder.contextGroup.postDelayed(hideAction, 5000); 187 return true; 188 } 189 }); 190 holder.delete.setOnClickListener(new View.OnClickListener() { 191 @Override 192 public void onClick(View v) { 193 holder.contextGroup.setVisibility(View.INVISIBLE); 194 holder.contextGroup.removeCallbacks((Runnable) holder.contextGroup.getTag()); 195 onCatRemove(mCats[holder.getAdapterPosition()]); 196 } 197 }); 198 holder.share.setOnClickListener(new View.OnClickListener() { 199 @Override 200 public void onClick(View v) { 201 Cat cat = mCats[holder.getAdapterPosition()]; 202 if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) 203 != PackageManager.PERMISSION_GRANTED) { 204 mPendingShareCat = cat; 205 requestPermissions( 206 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 207 STORAGE_PERM_REQUEST); 208 return; 209 } 210 shareCat(cat); 211 } 212 }); 213 } 214 215 @Override 216 public int getItemCount() { 217 return mCats.length; 218 } 219 } 220 221 private void shareCat(Cat cat) { 222 final File dir = new File( 223 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), 224 getString(R.string.directory_name)); 225 if (!dir.exists() && !dir.mkdirs()) { 226 Log.e("NekoLand", "save: error: can't create Pictures directory"); 227 return; 228 } 229 final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png"); 230 Bitmap bitmap = cat.createBitmap(512, 512); 231 if (bitmap != null) { 232 try { 233 OutputStream os = new FileOutputStream(png); 234 bitmap.compress(Bitmap.CompressFormat.PNG, 0, os); 235 os.close(); 236 MediaScannerConnection.scanFile( 237 this, 238 new String[] {png.toString()}, 239 new String[] {"image/png"}, 240 null); 241 Uri uri = Uri.fromFile(png); 242 Intent intent = new Intent(Intent.ACTION_SEND); 243 intent.putExtra(Intent.EXTRA_STREAM, uri); 244 intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName()); 245 intent.setType("image/png"); 246 startActivity(Intent.createChooser(intent, null)); 247 } catch (IOException e) { 248 Log.e("NekoLand", "save: error: " + e); 249 } 250 } 251 } 252 253 @Override 254 public void onRequestPermissionsResult(int requestCode, 255 String permissions[], int[] grantResults) { 256 if (requestCode == STORAGE_PERM_REQUEST) { 257 if (mPendingShareCat != null) { 258 shareCat(mPendingShareCat); 259 mPendingShareCat = null; 260 } 261 } 262 } 263 264 private static class CatHolder extends RecyclerView.ViewHolder { 265 private final ImageView imageView; 266 private final TextView textView; 267 private final View contextGroup; 268 private final View delete; 269 private final View share; 270 271 public CatHolder(View itemView) { 272 super(itemView); 273 imageView = (ImageView) itemView.findViewById(android.R.id.icon); 274 textView = (TextView) itemView.findViewById(android.R.id.title); 275 contextGroup = itemView.findViewById(R.id.contextGroup); 276 delete = itemView.findViewById(android.R.id.closeButton); 277 share = itemView.findViewById(android.R.id.shareText); 278 } 279 } 280} 281