/*
* Copyright (C) 2014 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com>
*
* This file is part of Amaze File Manager.
*
* Amaze File Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.amaze.filemanager.activities;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ContentProviderClient;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.BackgroundColorSpan;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.amaze.filemanager.R;
import com.amaze.filemanager.exceptions.RootNotPermittedException;
import com.amaze.filemanager.exceptions.StreamNotFoundException;
import com.amaze.filemanager.filesystem.BaseFile;
import com.amaze.filemanager.filesystem.FileUtil;
import com.amaze.filemanager.services.asynctasks.SearchTextTask;
import com.amaze.filemanager.ui.dialogs.GeneralDialogCreation;
import com.amaze.filemanager.utils.GenericCopyUtil;
import com.amaze.filemanager.utils.MapEntry;
import com.amaze.filemanager.utils.PreferenceUtils;
import com.amaze.filemanager.utils.RootUtils;
import com.amaze.filemanager.utils.Utils;
import com.amaze.filemanager.utils.color.ColorUsage;
import com.amaze.filemanager.utils.theme.AppTheme;
import com.readystatesoftware.systembartint.SystemBarTintManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class TextReader extends BaseActivity implements TextWatcher, View.OnClickListener {
public EditText mInput, searchEditText;
private BaseFile mFile;
private String mOriginal;
private Timer mTimer;
private boolean mModified, isEditAllowed = true;
private Typeface mInputTypefaceDefault, mInputTypefaceMono;
private android.support.v7.widget.Toolbar toolbar;
//ArrayList<StringBuilder> texts;
//static final int maxlength = 200;
//int index = 0;
ScrollView scrollView;
/*
* List maintaining the searched text's start/end index as key/value pair
*/
public ArrayList<MapEntry> nodes = new ArrayList<>();
/*
* variable to maintain the position of index
* while pressing next/previous button in the searchBox
*/
private int mCurrent = -1;
/*
* variable to maintain line number of the searched phrase
* further used to calculate the scroll position
*/
public int mLine = 0;
private SearchTextTask searchTextTask;
private static final String KEY_MODIFIED_TEXT = "modified";
private static final String KEY_INDEX = "index";
private static final String KEY_ORIGINAL_TEXT = "original";
private static final String KEY_MONOFONT = "monofont";
private RelativeLayout searchViewLayout;
Uri uri = null;
public ImageButton upButton, downButton, closeButton;
// input stream associated with the file
private InputStream inputStream;
private ParcelFileDescriptor parcelFileDescriptor;
private File cacheFile; // represents a file saved in cache
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
if (getAppTheme().equals(AppTheme.DARK))
getWindow().getDecorView().setBackgroundColor(Utils.getColor(this, R.color.holo_dark_background));
setContentView(R.layout.search);
searchViewLayout = (RelativeLayout) findViewById(R.id.searchview);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//findViewById(R.id.lin).setBackgroundColor(Color.parseColor(skin));
toolbar.setBackgroundColor(getColorPreference().getColor(ColorUsage.getPrimary(MainActivity.currentTab)));
searchViewLayout.setBackgroundColor(getColorPreference().getColor(ColorUsage.getPrimary(MainActivity.currentTab)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription("Amaze",
((BitmapDrawable) getResources().getDrawable(R.mipmap.ic_launcher)).getBitmap(),
getColorPreference().getColor(ColorUsage.getPrimary(MainActivity.currentTab)));
((Activity) this).setTaskDescription(taskDescription);
}
searchEditText = (EditText) searchViewLayout.findViewById(R.id.search_box);
upButton = (ImageButton) searchViewLayout.findViewById(R.id.prev);
downButton = (ImageButton) searchViewLayout.findViewById(R.id.next);
closeButton = (ImageButton) searchViewLayout.findViewById(R.id.close);
searchEditText.addTextChangedListener(this);
upButton.setOnClickListener(this);
//upButton.setEnabled(false);
downButton.setOnClickListener(this);
//downButton.setEnabled(false);
closeButton.setOnClickListener(this);
getSupportActionBar().setBackgroundDrawable(getColorPreference().getDrawable(ColorUsage.getPrimary(MainActivity.currentTab)));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
int sdk = Build.VERSION.SDK_INT;
if (sdk == Build.VERSION_CODES.KITKAT_WATCH || sdk == Build.VERSION_CODES.KITKAT) {
SystemBarTintManager tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintColor(getColorPreference().getColor(ColorUsage.getPrimary(MainActivity.currentTab)));
FrameLayout.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) findViewById(R.id.texteditor).getLayoutParams();
SystemBarTintManager.SystemBarConfig config = tintManager.getConfig();
p.setMargins(0, config.getStatusBarHeight(), 0, 0);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
boolean colourednavigation = sharedPref.getBoolean("colorednavigation", true);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(PreferenceUtils.getStatusColor(getColorPreference().getColorAsString(ColorUsage.getPrimary(MainActivity.currentTab))));
if (colourednavigation)
window.setNavigationBarColor(PreferenceUtils.getStatusColor(getColorPreference().getColorAsString(ColorUsage.getPrimary(MainActivity.currentTab))));
}
mInput = (EditText) findViewById(R.id.fname);
scrollView = (ScrollView) findViewById(R.id.editscroll);
if (getIntent().getData() != null) {
// getting uri from external source
uri = getIntent().getData();
mFile = new BaseFile(getIntent().getData().getPath());
}
String fileName;
// try getting filename from file system
fileName = mFile.getName();
try {
// try to get file name from content providers row
if (fileName == null && uri != null) {
if (uri.getScheme().equals("file")) {
fileName = uri.getLastPathSegment();
}
ContentProviderClient client = null;
Cursor cursor = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
client = getContentResolver().acquireUnstableContentProviderClient(uri);
} else {
throw new Exception();
}
cursor = client.query(uri, new String[]{
MediaStore.Images.ImageColumns.DISPLAY_NAME
}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DISPLAY_NAME));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
fileName = getString(R.string.error);
}
getSupportActionBar().setTitle(fileName);
mInput.addTextChangedListener(this);
try {
if (getAppTheme().equals(AppTheme.DARK))
mInput.setBackgroundColor(Utils.getColor(this, R.color.holo_dark_background));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch (Exception e) {
}
mInputTypefaceDefault = mInput.getTypeface();
mInputTypefaceMono = Typeface.MONOSPACE;
if (savedInstanceState != null) {
mOriginal = savedInstanceState.getString(KEY_ORIGINAL_TEXT);
int index = savedInstanceState.getInt(KEY_INDEX);
mInput.setText(savedInstanceState.getString(KEY_MODIFIED_TEXT));
mInput.setScrollY(index);
if (savedInstanceState.getBoolean(KEY_MONOFONT)) mInput.setTypeface(mInputTypefaceMono);
} else {
load(uri, mFile);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_MODIFIED_TEXT, mInput.getText().toString());
outState.putInt(KEY_INDEX, mInput.getScrollY());
outState.putString(KEY_ORIGINAL_TEXT, mOriginal);
outState.putBoolean(KEY_MONOFONT, mInput.getTypeface().equals(mInputTypefaceMono));
}
private void checkUnsavedChanges() {
if (mOriginal != null && mInput.isShown() && !mOriginal.equals(mInput.getText().toString())) {
new MaterialDialog.Builder(this)
.title(R.string.unsavedchanges)
.content(R.string.unsavedchangesdesc)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.positiveColor(Color.parseColor(accentSkin))
.negativeColor(Color.parseColor(accentSkin))
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
saveFile(uri, new File(mFile.getPath()), mInput.getText().toString());
finish();
}
@Override
public void onNegative(MaterialDialog dialog) {
finish();
}
})
.build().show();
} else {
finish();
}
}
/**
* Method initiates a worker thread which writes the {@link #mInput} bytes to the defined
* file/uri 's output stream
*
* @param uri the uri associated with this text (if any)
* @param file the file associated with this text (if any)
* @param editTextString the edit text string
*/
private void saveFile(final Uri uri, final File file, final String editTextString) {
Toast.makeText(this, R.string.saving, Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
try {
writeTextFile(uri, file, editTextString);
} catch (StreamNotFoundException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), R.string.error_file_not_found,
Toast.LENGTH_SHORT).show();
}
});
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), R.string.error_io,
Toast.LENGTH_SHORT).show();
}
});
} catch (RootNotPermittedException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), R.string.rootfailure,
Toast.LENGTH_SHORT).show();
}
});
}
}
}).start();
}
/**
* Helper method for {@link #saveFile(Uri, File, String)}
* Works on a background thread to save data to output stream associated with this reader
*
* @param uri
* @param file
* @param inputText
* @throws StreamNotFoundException
* @throws IOException
* @throws RootNotPermittedException
* @see #saveFile(Uri, File, String)
*/
private void writeTextFile(final Uri uri, final File file, String inputText)
throws StreamNotFoundException, IOException, RootNotPermittedException {
OutputStream outputStream = null;
if (uri.toString().contains("file://")) {
// dealing with files
try {
outputStream = FileUtil.getOutputStream(file, this);
} catch (Exception e) {
outputStream = null;
}
if (BaseActivity.rootMode && outputStream == null) {
// try loading stream associated using root
try {
if (cacheFile != null && cacheFile.exists())
outputStream = new FileOutputStream(cacheFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
outputStream = null;
}
}
} else if (uri.toString().contains("content://")) {
if (parcelFileDescriptor != null) {
File descriptorFile = new File(GenericCopyUtil.PATH_FILE_DESCRIPTOR + parcelFileDescriptor.getFd());
try {
outputStream = new FileOutputStream(descriptorFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
outputStream = null;
}
}
if (outputStream == null) {
try {
outputStream = getContentResolver().openOutputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
outputStream = null;
}
}
}
if (outputStream == null) throw new StreamNotFoundException();
// saving data to file
outputStream.write(inputText.getBytes());
outputStream.close();
mOriginal = inputText;
mModified = false;
invalidateOptionsMenu();
if (cacheFile != null && cacheFile.exists()) {
// cat cache content to original file and delete cache file
RootUtils.cat(cacheFile.getPath(), mFile.getPath());
cacheFile.delete();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), getString(R.string.done), Toast.LENGTH_SHORT).show();
}
});
}
private void setProgress(boolean show) {
//mInput.setVisibility(show ? View.GONE : View.VISIBLE);
// findViewById(R.id.progress).setVisibility(show ? View.VISIBLE : View.GONE);
}
/**
* Initiates loading of file/uri by getting an input stream associated with it
* on a worker thread
*
* @param uri
* @param mFile
*/
private void load(final Uri uri, final BaseFile mFile) {
setProgress(true);
this.mFile = mFile;
mInput.setHint(R.string.loading);
new Thread(new Runnable() {
@Override
public void run() {
try {
inputStream = getInputStream(uri, mFile);
String str;
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
if (bufferedReader != null) {
while ((str = bufferedReader.readLine()) != null) {
stringBuilder.append(str).append("\n");
}
}
mOriginal = stringBuilder.toString();
inputStream.close();
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
mInput.setText(mOriginal);
if (mOriginal.isEmpty()) {
mInput.setHint(R.string.file_empty);
} else
mInput.setHint(null);
} catch (OutOfMemoryError e) {
mInput.setHint(R.string.error);
}
setProgress(false);
}
});
} catch (StreamNotFoundException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
mInput.setHint(R.string.error_file_not_found);
}
});
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
mInput.setHint(R.string.error_io);
}
});
}
}
}).start();
}
@Override
public void onBackPressed() {
checkUnsavedChanges();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.text, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.save).setVisible(mModified);
menu.findItem(R.id.monofont).setChecked(mInput.getTypeface().equals(mInputTypefaceMono));
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
checkUnsavedChanges();
break;
case R.id.save:
// Make sure EditText is visible before saving!
saveFile(uri, new File(mFile.getPath()), mInput.getText().toString());
break;
case R.id.details:
if (mFile.exists()) {
//HFile hFile = new HFile(OpenMode.FILE, mFile.getPath());
//hFile.generateMode(this);
GeneralDialogCreation.showPropertiesDialogWithoutPermissions(mFile, this, getAppTheme());
} else Toast.makeText(this, R.string.not_allowed, Toast.LENGTH_SHORT).show();
break;
case R.id.openwith:
if (mFile.exists()) {
getFutils().openunknown(new File(mFile.getPath()), this, false);
} else Toast.makeText(this, R.string.not_allowed, Toast.LENGTH_SHORT).show();
break;
case R.id.find:
if (searchViewLayout.isShown()) hideSearchView();
else revealSearchView();
break;
case R.id.monofont:
item.setChecked(!item.isChecked());
mInput.setTypeface(item.isChecked() ? mInputTypefaceMono : mInputTypefaceDefault);
break;
default:
return false;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onDestroy() {
super.onDestroy();
// closing parcel file descriptor if associated any
if (parcelFileDescriptor != null) {
try {
parcelFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, getString(R.string.error_io), Toast.LENGTH_LONG).show();
}
}
if (cacheFile != null && cacheFile.exists()) cacheFile.delete();
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
// condition to check if callback is called in search editText
if (searchEditText != null && charSequence.hashCode() == searchEditText.getText().hashCode()) {
// clearing before adding new values
if (searchTextTask != null) searchTextTask.cancel(true);
cleanSpans();
}
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
if (charSequence.hashCode() == mInput.getText().hashCode()) {
if (mTimer != null) {
mTimer.cancel();
mTimer.purge();
mTimer = null;
}
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
boolean modified;
@Override
public void run() {
modified = !mInput.getText().toString().equals(mOriginal);
if (mModified != modified) {
mModified = modified;
invalidateOptionsMenu();
}
}
}, 250);
}
}
@Override
public void afterTextChanged(Editable editable) {
// searchBox callback block
if (searchEditText != null && editable.hashCode() == searchEditText.getText().hashCode()) {
searchTextTask = new SearchTextTask(this);
searchTextTask.execute(editable);
}
}
/**
* Helper method to {@link #load(Uri, BaseFile)}
* Tries to find an input stream associated with file/uri
*
* @param uri
* @param baseFile
* @return
* @throws StreamNotFoundException exception thrown when we couldn't find a stream
* after all the attempts
*/
private InputStream getInputStream(Uri uri, BaseFile baseFile) throws StreamNotFoundException {
InputStream stream = null;
File file = new File(baseFile.getPath());
if (uri.toString().contains("file://")) {
// dealing with files
if (!file.canWrite() && BaseActivity.rootMode) {
// try loading stream associated using root
try {
File cacheDir = getExternalCacheDir();
cacheFile = new File(cacheDir, mFile.getName());
// creating a cache file
RootUtils.copy(mFile.getPath(), cacheFile.getPath());
try {
stream = new FileInputStream(cacheFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
stream = null;
}
} catch (RootNotPermittedException e) {
e.printStackTrace();
stream = null;
}
} else if (file.canRead()) {
// readable file in filesystem
try {
stream = new FileInputStream(file.getPath());
} catch (FileNotFoundException e) {
stream = null;
}
}
} else if (uri.toString().contains("content://")) {
// dealing with content provider trying to get URI from intent action
try {
// getting a writable file descriptor
parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "rw");
File parcelFile = new File(GenericCopyUtil.PATH_FILE_DESCRIPTOR + parcelFileDescriptor.getFd());
stream = new FileInputStream(parcelFile);
} catch (FileNotFoundException e) {
// falling back to readable file descriptor
try {
parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");
File parcelFile = new File(GenericCopyUtil.PATH_FILE_DESCRIPTOR + parcelFileDescriptor.getFd());
stream = new FileInputStream(parcelFile);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
stream = null;
}
}
if (stream == null) {
// couldn't get a file descriptor based on path, let's try opening stream directly
try {
stream = getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
stream = null;
}
}
}
// throwing exception if stream not found after all the attempts above
if (stream == null) throw new StreamNotFoundException();
return stream;
}
/**
* show search view with a circular reveal animation
*/
void revealSearchView() {
int startRadius = 4;
int endRadius = Math.max(searchViewLayout.getWidth(), searchViewLayout.getHeight());
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
// hardcoded and completely random
int cx = metrics.widthPixels - 160;
int cy = toolbar.getBottom();
Animator animator;
// FIXME: 2016/11/18 ViewAnimationUtils Compatibility
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
animator = ViewAnimationUtils.createCircularReveal(searchViewLayout, cx, cy,
startRadius, endRadius);
else
animator = ObjectAnimator.ofFloat(searchViewLayout, "alpha", 0f, 1f);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(600);
searchViewLayout.setVisibility(View.VISIBLE);
searchEditText.setText("");
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
searchEditText.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
/**
* hide search view with a circular reveal animation
*/
void hideSearchView() {
int endRadius = 4;
int startRadius = Math.max(searchViewLayout.getWidth(), searchViewLayout.getHeight());
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
// hardcoded and completely random
int cx = metrics.widthPixels - 160;
int cy = toolbar.getBottom();
Animator animator;
// FIXME: 2016/11/18 ViewAnimationUtils Compatibility
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
animator = ViewAnimationUtils.createCircularReveal(searchViewLayout, cx, cy,
startRadius, endRadius);
} else {
animator = ObjectAnimator.ofFloat(searchViewLayout, "alpha", 0f, 1f);
}
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(600);
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
searchViewLayout.setVisibility(View.GONE);
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(searchEditText.getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.prev:
// upButton
if (mCurrent > 0) {
// setting older span back before setting new one
Map.Entry keyValueOld = (Map.Entry) nodes.get(mCurrent).getKey();
mInput.getText().setSpan(getAppTheme().equals(AppTheme.LIGHT) ? new BackgroundColorSpan(Color.YELLOW) :
new BackgroundColorSpan(Color.LTGRAY),
(Integer) keyValueOld.getKey(),
(Integer) keyValueOld.getValue(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
// highlighting previous element in list
Map.Entry keyValueNew = (Map.Entry) nodes.get(--mCurrent).getKey();
mInput.getText().setSpan(new BackgroundColorSpan(Utils.getColor(this, R.color.search_text_highlight)),
(Integer) keyValueNew.getKey(),
(Integer) keyValueNew.getValue(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
// scrolling to the highlighted element
scrollView.scrollTo(0, (Integer) keyValueNew.getValue()
+ mInput.getLineHeight() + Math.round(mInput.getLineSpacingExtra())
- getSupportActionBar().getHeight());
}
break;
case R.id.next:
// downButton
if (mCurrent < nodes.size() - 1) {
// setting older span back before setting new one
if (mCurrent != -1) {
Map.Entry keyValueOld = (Map.Entry) nodes.get(mCurrent).getKey();
mInput.getText().setSpan(getAppTheme().equals(AppTheme.LIGHT) ? new BackgroundColorSpan(Color.YELLOW) :
new BackgroundColorSpan(Color.LTGRAY),
(Integer) keyValueOld.getKey(),
(Integer) keyValueOld.getValue(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
Map.Entry keyValueNew = (Map.Entry) nodes.get(++mCurrent).getKey();
mInput.getText().setSpan(new BackgroundColorSpan(Utils.getColor(this, R.color.search_text_highlight)),
(Integer) keyValueNew.getKey(),
(Integer) keyValueNew.getValue(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
// scrolling to the highlighted element
scrollView.scrollTo(0, (Integer) keyValueNew.getValue()
+ mInput.getLineHeight() + Math.round(mInput.getLineSpacingExtra())
- getSupportActionBar().getHeight());
}
break;
case R.id.close:
// closeButton
findViewById(R.id.searchview).setVisibility(View.GONE);
cleanSpans();
break;
}
}
private void cleanSpans() {
// resetting current highlight and line number
nodes.clear();
mCurrent = -1;
mLine = 0;
// clearing textView spans
BackgroundColorSpan[] colorSpans = mInput.getText().getSpans(0,
mInput.length(), BackgroundColorSpan.class);
for (BackgroundColorSpan colorSpan : colorSpans) {
mInput.getText().removeSpan(colorSpan);
}
}
public int getLineNumber() {
return this.mLine;
}
public void setLineNumber(int lineNumber) {
this.mLine = lineNumber;
}
}