@ -0,0 +1,24 @@
|
||||
package net.micode.notes;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_main);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.DialogInterface.OnDismissListener;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.tool.DataUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
|
||||
private long mNoteId;
|
||||
private String mSnippet;
|
||||
private static final int SNIPPET_PREW_MAX_LEN = 60;
|
||||
MediaPlayer mPlayer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
final Window win = getWindow();
|
||||
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
|
||||
if (!isScreenOn()) {
|
||||
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
try {
|
||||
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
|
||||
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
|
||||
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
|
||||
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
|
||||
: mSnippet;
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
mPlayer = new MediaPlayer();
|
||||
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
|
||||
showActionDialog();
|
||||
playAlarmSound();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isScreenOn() {
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
return pm.isScreenOn();
|
||||
}
|
||||
|
||||
private void playAlarmSound() {
|
||||
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
|
||||
|
||||
int silentModeStreams = Settings.System.getInt(getContentResolver(),
|
||||
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
|
||||
|
||||
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
|
||||
mPlayer.setAudioStreamType(silentModeStreams);
|
||||
} else {
|
||||
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
|
||||
}
|
||||
try {
|
||||
mPlayer.setDataSource(this, url);
|
||||
mPlayer.prepare();
|
||||
mPlayer.setLooping(true);
|
||||
mPlayer.start();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void showActionDialog() {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||
dialog.setTitle(R.string.app_name);
|
||||
dialog.setMessage(mSnippet);
|
||||
dialog.setPositiveButton(R.string.notealert_ok, this);
|
||||
if (isScreenOn()) {
|
||||
dialog.setNegativeButton(R.string.notealert_enter, this);
|
||||
}
|
||||
dialog.show().setOnDismissListener(this);
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
Intent intent = new Intent(this, NoteEditActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.putExtra(Intent.EXTRA_UID, mNoteId);
|
||||
startActivity(intent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
stopAlarmSound();
|
||||
finish();
|
||||
}
|
||||
|
||||
private void stopAlarmSound() {
|
||||
if (mPlayer != null) {
|
||||
mPlayer.stop();
|
||||
mPlayer.release();
|
||||
mPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
|
||||
|
||||
public class AlarmInitReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String [] PROJECTION = new String [] {
|
||||
NoteColumns.ID,
|
||||
NoteColumns.ALERTED_DATE
|
||||
};
|
||||
|
||||
private static final int COLUMN_ID = 0;
|
||||
private static final int COLUMN_ALERTED_DATE = 1;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
long currentDate = System.currentTimeMillis();
|
||||
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
|
||||
PROJECTION,
|
||||
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
|
||||
new String[] { String.valueOf(currentDate) },
|
||||
null);
|
||||
|
||||
if (c != null) {
|
||||
if (c.moveToFirst()) {
|
||||
do {
|
||||
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
|
||||
Intent sender = new Intent(context, AlarmReceiver.class);
|
||||
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, PendingIntent.FLAG_IMMUTABLE);
|
||||
AlarmManager alermManager = (AlarmManager) context
|
||||
.getSystemService(Context.ALARM_SERVICE);
|
||||
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
intent.setClass(context, AlarmAlertActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.util.Calendar;
|
||||
|
||||
import net.micode.notes.R;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
public class DateTimePicker extends FrameLayout {
|
||||
|
||||
private static final boolean DEFAULT_ENABLE_STATE = true;
|
||||
|
||||
private static final int HOURS_IN_HALF_DAY = 12;
|
||||
private static final int HOURS_IN_ALL_DAY = 24;
|
||||
private static final int DAYS_IN_ALL_WEEK = 7;
|
||||
private static final int DATE_SPINNER_MIN_VAL = 0;
|
||||
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
|
||||
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
|
||||
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
|
||||
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
|
||||
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
|
||||
private static final int MINUT_SPINNER_MIN_VAL = 0;
|
||||
private static final int MINUT_SPINNER_MAX_VAL = 59;
|
||||
private static final int AMPM_SPINNER_MIN_VAL = 0;
|
||||
private static final int AMPM_SPINNER_MAX_VAL = 1;
|
||||
|
||||
private final NumberPicker mDateSpinner;
|
||||
private final NumberPicker mHourSpinner;
|
||||
private final NumberPicker mMinuteSpinner;
|
||||
private final NumberPicker mAmPmSpinner;
|
||||
private Calendar mDate;
|
||||
|
||||
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
|
||||
|
||||
private boolean mIsAm;
|
||||
|
||||
private boolean mIs24HourView;
|
||||
|
||||
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
|
||||
|
||||
private boolean mInitialising;
|
||||
|
||||
private OnDateTimeChangedListener mOnDateTimeChangedListener;
|
||||
|
||||
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
|
||||
@Override
|
||||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
|
||||
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
|
||||
updateDateControl();
|
||||
onDateTimeChanged();
|
||||
}
|
||||
};
|
||||
|
||||
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
|
||||
@Override
|
||||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
|
||||
boolean isDateChanged = false;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
if (!mIs24HourView) {
|
||||
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
|
||||
cal.setTimeInMillis(mDate.getTimeInMillis());
|
||||
cal.add(Calendar.DAY_OF_YEAR, 1);
|
||||
isDateChanged = true;
|
||||
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
|
||||
cal.setTimeInMillis(mDate.getTimeInMillis());
|
||||
cal.add(Calendar.DAY_OF_YEAR, -1);
|
||||
isDateChanged = true;
|
||||
}
|
||||
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
|
||||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
|
||||
mIsAm = !mIsAm;
|
||||
updateAmPmControl();
|
||||
}
|
||||
} else {
|
||||
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
|
||||
cal.setTimeInMillis(mDate.getTimeInMillis());
|
||||
cal.add(Calendar.DAY_OF_YEAR, 1);
|
||||
isDateChanged = true;
|
||||
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
|
||||
cal.setTimeInMillis(mDate.getTimeInMillis());
|
||||
cal.add(Calendar.DAY_OF_YEAR, -1);
|
||||
isDateChanged = true;
|
||||
}
|
||||
}
|
||||
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
|
||||
mDate.set(Calendar.HOUR_OF_DAY, newHour);
|
||||
onDateTimeChanged();
|
||||
if (isDateChanged) {
|
||||
setCurrentYear(cal.get(Calendar.YEAR));
|
||||
setCurrentMonth(cal.get(Calendar.MONTH));
|
||||
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
|
||||
@Override
|
||||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
|
||||
int minValue = mMinuteSpinner.getMinValue();
|
||||
int maxValue = mMinuteSpinner.getMaxValue();
|
||||
int offset = 0;
|
||||
if (oldVal == maxValue && newVal == minValue) {
|
||||
offset += 1;
|
||||
} else if (oldVal == minValue && newVal == maxValue) {
|
||||
offset -= 1;
|
||||
}
|
||||
if (offset != 0) {
|
||||
mDate.add(Calendar.HOUR_OF_DAY, offset);
|
||||
mHourSpinner.setValue(getCurrentHour());
|
||||
updateDateControl();
|
||||
int newHour = getCurrentHourOfDay();
|
||||
if (newHour >= HOURS_IN_HALF_DAY) {
|
||||
mIsAm = false;
|
||||
updateAmPmControl();
|
||||
} else {
|
||||
mIsAm = true;
|
||||
updateAmPmControl();
|
||||
}
|
||||
}
|
||||
mDate.set(Calendar.MINUTE, newVal);
|
||||
onDateTimeChanged();
|
||||
}
|
||||
};
|
||||
|
||||
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
|
||||
@Override
|
||||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
|
||||
mIsAm = !mIsAm;
|
||||
if (mIsAm) {
|
||||
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
|
||||
} else {
|
||||
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
|
||||
}
|
||||
updateAmPmControl();
|
||||
onDateTimeChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public interface OnDateTimeChangedListener {
|
||||
void onDateTimeChanged(DateTimePicker view, int year, int month,
|
||||
int dayOfMonth, int hourOfDay, int minute);
|
||||
}
|
||||
|
||||
public DateTimePicker(Context context) {
|
||||
this(context, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public DateTimePicker(Context context, long date) {
|
||||
this(context, date, DateFormat.is24HourFormat(context));
|
||||
}
|
||||
|
||||
public DateTimePicker(Context context, long date, boolean is24HourView) {
|
||||
super(context);
|
||||
mDate = Calendar.getInstance();
|
||||
mInitialising = true;
|
||||
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
|
||||
inflate(context, R.layout.datetime_picker, this);
|
||||
|
||||
mDateSpinner = (NumberPicker) findViewById(R.id.date);
|
||||
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
|
||||
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
|
||||
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
|
||||
|
||||
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
|
||||
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
|
||||
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
|
||||
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
|
||||
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
|
||||
mMinuteSpinner.setOnLongPressUpdateInterval(100);
|
||||
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
|
||||
|
||||
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
|
||||
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
|
||||
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
|
||||
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
|
||||
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
|
||||
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
|
||||
|
||||
// update controls to initial state
|
||||
updateDateControl();
|
||||
updateHourControl();
|
||||
updateAmPmControl();
|
||||
|
||||
set24HourView(is24HourView);
|
||||
|
||||
// set to current time
|
||||
setCurrentDate(date);
|
||||
|
||||
setEnabled(isEnabled());
|
||||
|
||||
// set the content descriptions
|
||||
mInitialising = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (mIsEnabled == enabled) {
|
||||
return;
|
||||
}
|
||||
super.setEnabled(enabled);
|
||||
mDateSpinner.setEnabled(enabled);
|
||||
mMinuteSpinner.setEnabled(enabled);
|
||||
mHourSpinner.setEnabled(enabled);
|
||||
mAmPmSpinner.setEnabled(enabled);
|
||||
mIsEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return mIsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current date in millis
|
||||
*
|
||||
* @return the current date in millis
|
||||
*/
|
||||
public long getCurrentDateInTimeMillis() {
|
||||
return mDate.getTimeInMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current date
|
||||
*
|
||||
* @param date The current date in millis
|
||||
*/
|
||||
public void setCurrentDate(long date) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(date);
|
||||
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
|
||||
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current date
|
||||
*
|
||||
* @param year The current year
|
||||
* @param month The current month
|
||||
* @param dayOfMonth The current dayOfMonth
|
||||
* @param hourOfDay The current hourOfDay
|
||||
* @param minute The current minute
|
||||
*/
|
||||
public void setCurrentDate(int year, int month,
|
||||
int dayOfMonth, int hourOfDay, int minute) {
|
||||
setCurrentYear(year);
|
||||
setCurrentMonth(month);
|
||||
setCurrentDay(dayOfMonth);
|
||||
setCurrentHour(hourOfDay);
|
||||
setCurrentMinute(minute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current year
|
||||
*
|
||||
* @return The current year
|
||||
*/
|
||||
public int getCurrentYear() {
|
||||
return mDate.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current year
|
||||
*
|
||||
* @param year The current year
|
||||
*/
|
||||
public void setCurrentYear(int year) {
|
||||
if (!mInitialising && year == getCurrentYear()) {
|
||||
return;
|
||||
}
|
||||
mDate.set(Calendar.YEAR, year);
|
||||
updateDateControl();
|
||||
onDateTimeChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current month in the year
|
||||
*
|
||||
* @return The current month in the year
|
||||
*/
|
||||
public int getCurrentMonth() {
|
||||
return mDate.get(Calendar.MONTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current month in the year
|
||||
*
|
||||
* @param month The month in the year
|
||||
*/
|
||||
public void setCurrentMonth(int month) {
|
||||
if (!mInitialising && month == getCurrentMonth()) {
|
||||
return;
|
||||
}
|
||||
mDate.set(Calendar.MONTH, month);
|
||||
updateDateControl();
|
||||
onDateTimeChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current day of the month
|
||||
*
|
||||
* @return The day of the month
|
||||
*/
|
||||
public int getCurrentDay() {
|
||||
return mDate.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current day of the month
|
||||
*
|
||||
* @param dayOfMonth The day of the month
|
||||
*/
|
||||
public void setCurrentDay(int dayOfMonth) {
|
||||
if (!mInitialising && dayOfMonth == getCurrentDay()) {
|
||||
return;
|
||||
}
|
||||
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
updateDateControl();
|
||||
onDateTimeChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current hour in 24 hour mode, in the range (0~23)
|
||||
* @return The current hour in 24 hour mode
|
||||
*/
|
||||
public int getCurrentHourOfDay() {
|
||||
return mDate.get(Calendar.HOUR_OF_DAY);
|
||||
}
|
||||
|
||||
private int getCurrentHour() {
|
||||
if (mIs24HourView){
|
||||
return getCurrentHourOfDay();
|
||||
} else {
|
||||
int hour = getCurrentHourOfDay();
|
||||
if (hour > HOURS_IN_HALF_DAY) {
|
||||
return hour - HOURS_IN_HALF_DAY;
|
||||
} else {
|
||||
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current hour in 24 hour mode, in the range (0~23)
|
||||
*
|
||||
* @param hourOfDay
|
||||
*/
|
||||
public void setCurrentHour(int hourOfDay) {
|
||||
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
|
||||
return;
|
||||
}
|
||||
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||
if (!mIs24HourView) {
|
||||
if (hourOfDay >= HOURS_IN_HALF_DAY) {
|
||||
mIsAm = false;
|
||||
if (hourOfDay > HOURS_IN_HALF_DAY) {
|
||||
hourOfDay -= HOURS_IN_HALF_DAY;
|
||||
}
|
||||
} else {
|
||||
mIsAm = true;
|
||||
if (hourOfDay == 0) {
|
||||
hourOfDay = HOURS_IN_HALF_DAY;
|
||||
}
|
||||
}
|
||||
updateAmPmControl();
|
||||
}
|
||||
mHourSpinner.setValue(hourOfDay);
|
||||
onDateTimeChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currentMinute
|
||||
*
|
||||
* @return The Current Minute
|
||||
*/
|
||||
public int getCurrentMinute() {
|
||||
return mDate.get(Calendar.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current minute
|
||||
*/
|
||||
public void setCurrentMinute(int minute) {
|
||||
if (!mInitialising && minute == getCurrentMinute()) {
|
||||
return;
|
||||
}
|
||||
mMinuteSpinner.setValue(minute);
|
||||
mDate.set(Calendar.MINUTE, minute);
|
||||
onDateTimeChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this is in 24 hour view else false.
|
||||
*/
|
||||
public boolean is24HourView () {
|
||||
return mIs24HourView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether in 24 hour or AM/PM mode.
|
||||
*
|
||||
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
|
||||
*/
|
||||
public void set24HourView(boolean is24HourView) {
|
||||
if (mIs24HourView == is24HourView) {
|
||||
return;
|
||||
}
|
||||
mIs24HourView = is24HourView;
|
||||
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
|
||||
int hour = getCurrentHourOfDay();
|
||||
updateHourControl();
|
||||
setCurrentHour(hour);
|
||||
updateAmPmControl();
|
||||
}
|
||||
|
||||
private void updateDateControl() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(mDate.getTimeInMillis());
|
||||
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
|
||||
mDateSpinner.setDisplayedValues(null);
|
||||
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
|
||||
cal.add(Calendar.DAY_OF_YEAR, 1);
|
||||
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
|
||||
}
|
||||
mDateSpinner.setDisplayedValues(mDateDisplayValues);
|
||||
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
|
||||
mDateSpinner.invalidate();
|
||||
}
|
||||
|
||||
private void updateAmPmControl() {
|
||||
if (mIs24HourView) {
|
||||
mAmPmSpinner.setVisibility(View.GONE);
|
||||
} else {
|
||||
int index = mIsAm ? Calendar.AM : Calendar.PM;
|
||||
mAmPmSpinner.setValue(index);
|
||||
mAmPmSpinner.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHourControl() {
|
||||
if (mIs24HourView) {
|
||||
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
|
||||
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
|
||||
} else {
|
||||
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
|
||||
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback that indicates the 'Set' button has been pressed.
|
||||
* @param callback the callback, if null will do nothing
|
||||
*/
|
||||
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
|
||||
mOnDateTimeChangedListener = callback;
|
||||
}
|
||||
|
||||
private void onDateTimeChanged() {
|
||||
if (mOnDateTimeChangedListener != null) {
|
||||
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
|
||||
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.ui.DateTimePicker;
|
||||
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
|
||||
|
||||
private Calendar mDate = Calendar.getInstance();
|
||||
private boolean mIs24HourView;
|
||||
private OnDateTimeSetListener mOnDateTimeSetListener;
|
||||
private DateTimePicker mDateTimePicker;
|
||||
|
||||
public interface OnDateTimeSetListener {
|
||||
void OnDateTimeSet(AlertDialog dialog, long date);
|
||||
}
|
||||
|
||||
public DateTimePickerDialog(Context context, long date) {
|
||||
super(context);
|
||||
mDateTimePicker = new DateTimePicker(context);
|
||||
setView(mDateTimePicker);
|
||||
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
|
||||
public void onDateTimeChanged(DateTimePicker view, int year, int month,
|
||||
int dayOfMonth, int hourOfDay, int minute) {
|
||||
mDate.set(Calendar.YEAR, year);
|
||||
mDate.set(Calendar.MONTH, month);
|
||||
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||
mDate.set(Calendar.MINUTE, minute);
|
||||
updateTitle(mDate.getTimeInMillis());
|
||||
}
|
||||
});
|
||||
mDate.setTimeInMillis(date);
|
||||
mDate.set(Calendar.SECOND, 0);
|
||||
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
|
||||
setButton(context.getString(R.string.datetime_dialog_ok), this);
|
||||
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
|
||||
set24HourView(DateFormat.is24HourFormat(this.getContext()));
|
||||
updateTitle(mDate.getTimeInMillis());
|
||||
}
|
||||
|
||||
public void set24HourView(boolean is24HourView) {
|
||||
mIs24HourView = is24HourView;
|
||||
}
|
||||
|
||||
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
|
||||
mOnDateTimeSetListener = callBack;
|
||||
}
|
||||
|
||||
private void updateTitle(long date) {
|
||||
int flag =
|
||||
DateUtils.FORMAT_SHOW_YEAR |
|
||||
DateUtils.FORMAT_SHOW_DATE |
|
||||
DateUtils.FORMAT_SHOW_TIME;
|
||||
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
|
||||
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface arg0, int arg1) {
|
||||
if (mOnDateTimeSetListener != null) {
|
||||
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.PopupMenu.OnMenuItemClickListener;
|
||||
|
||||
import net.micode.notes.R;
|
||||
|
||||
public class DropdownMenu {
|
||||
private Button mButton;
|
||||
private PopupMenu mPopupMenu;
|
||||
private Menu mMenu;
|
||||
|
||||
public DropdownMenu(Context context, Button button, int menuId) {
|
||||
mButton = button;
|
||||
mButton.setBackgroundResource(R.drawable.dropdown_icon);
|
||||
mPopupMenu = new PopupMenu(context, mButton);
|
||||
mMenu = mPopupMenu.getMenu();
|
||||
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
|
||||
mButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
mPopupMenu.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
|
||||
if (mPopupMenu != null) {
|
||||
mPopupMenu.setOnMenuItemClickListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public MenuItem findItem(int id) {
|
||||
return mMenu.findItem(id);
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mButton.setText(title);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
|
||||
|
||||
public class FoldersListAdapter extends CursorAdapter {
|
||||
public static final String [] PROJECTION = {
|
||||
NoteColumns.ID,
|
||||
NoteColumns.SNIPPET
|
||||
};
|
||||
|
||||
public static final int ID_COLUMN = 0;
|
||||
public static final int NAME_COLUMN = 1;
|
||||
|
||||
public FoldersListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return new FolderListItem(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
if (view instanceof FolderListItem) {
|
||||
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
|
||||
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
|
||||
((FolderListItem) view).bind(folderName);
|
||||
}
|
||||
}
|
||||
|
||||
public String getFolderName(Context context, int position) {
|
||||
Cursor cursor = (Cursor) getItem(position);
|
||||
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
|
||||
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
|
||||
}
|
||||
|
||||
private class FolderListItem extends LinearLayout {
|
||||
private TextView mName;
|
||||
|
||||
public FolderListItem(Context context) {
|
||||
super(context);
|
||||
inflate(context, R.layout.folder_list_item, this);
|
||||
mName = (TextView) findViewById(R.id.tv_folder_name);
|
||||
}
|
||||
|
||||
public void bind(String name) {
|
||||
mName.setText(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.text.Layout;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MenuItem.OnMenuItemClickListener;
|
||||
import android.view.MotionEvent;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import net.micode.notes.R;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NoteEditText extends AppCompatEditText {
|
||||
private static final String TAG = "NoteEditText";
|
||||
private int mIndex;
|
||||
private int mSelectionStartBeforeDelete;
|
||||
|
||||
private static final String SCHEME_TEL = "tel:" ;
|
||||
private static final String SCHEME_HTTP = "http:" ;
|
||||
private static final String SCHEME_EMAIL = "mailto:" ;
|
||||
|
||||
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
|
||||
static {
|
||||
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
|
||||
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
|
||||
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call by the {@link NoteEditActivity} to delete or add edit text
|
||||
*/
|
||||
public interface OnTextViewChangeListener {
|
||||
/**
|
||||
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
|
||||
* and the text is null
|
||||
*/
|
||||
void onEditTextDelete(int index, String text);
|
||||
|
||||
/**
|
||||
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
|
||||
* happen
|
||||
*/
|
||||
void onEditTextEnter(int index, String text);
|
||||
|
||||
/**
|
||||
* Hide or show item option when text change
|
||||
*/
|
||||
void onTextChange(int index, boolean hasText);
|
||||
}
|
||||
|
||||
private OnTextViewChangeListener mOnTextViewChangeListener;
|
||||
|
||||
public NoteEditText(Context context) {
|
||||
super(context, null);
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
|
||||
mOnTextViewChangeListener = listener;
|
||||
}
|
||||
|
||||
public NoteEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, android.R.attr.editTextStyle);
|
||||
}
|
||||
|
||||
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
x -= getTotalPaddingLeft();
|
||||
y -= getTotalPaddingTop();
|
||||
x += getScrollX();
|
||||
y += getScrollY();
|
||||
|
||||
Layout layout = getLayout();
|
||||
int line = layout.getLineForVertical(y);
|
||||
int off = layout.getOffsetForHorizontal(line, x);
|
||||
Selection.setSelection(getText(), off);
|
||||
break;
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
if (mOnTextViewChangeListener != null) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DEL:
|
||||
mSelectionStartBeforeDelete = getSelectionStart();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
switch(keyCode) {
|
||||
case KeyEvent.KEYCODE_DEL:
|
||||
if (mOnTextViewChangeListener != null) {
|
||||
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
|
||||
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "OnTextViewChangeListener was not seted");
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
if (mOnTextViewChangeListener != null) {
|
||||
int selectionStart = getSelectionStart();
|
||||
String text = getText().subSequence(selectionStart, length()).toString();
|
||||
setText(getText().subSequence(0, selectionStart));
|
||||
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
|
||||
} else {
|
||||
Log.d(TAG, "OnTextViewChangeListener was not seted");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
||||
if (mOnTextViewChangeListener != null) {
|
||||
if (!focused && TextUtils.isEmpty(getText())) {
|
||||
mOnTextViewChangeListener.onTextChange(mIndex, false);
|
||||
} else {
|
||||
mOnTextViewChangeListener.onTextChange(mIndex, true);
|
||||
}
|
||||
}
|
||||
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateContextMenu(ContextMenu menu) {
|
||||
if (getText() instanceof Spanned) {
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
|
||||
int min = Math.min(selStart, selEnd);
|
||||
int max = Math.max(selStart, selEnd);
|
||||
|
||||
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
|
||||
if (urls.length == 1) {
|
||||
int defaultResId = 0;
|
||||
for(String schema: sSchemaActionResMap.keySet()) {
|
||||
if(urls[0].getURL().indexOf(schema) >= 0) {
|
||||
defaultResId = sSchemaActionResMap.get(schema);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultResId == 0) {
|
||||
defaultResId = R.string.note_link_other;
|
||||
}
|
||||
|
||||
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
|
||||
new OnMenuItemClickListener() {
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
// goto a new intent
|
||||
urls[0].onClick(NoteEditText.this);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
super.onCreateContextMenu(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查选中文本是否具有特定样式
|
||||
*/
|
||||
public boolean hasStyle(int style) {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
StyleSpan[] spans = spanned.getSpans(selStart, selEnd, StyleSpan.class);
|
||||
|
||||
for (StyleSpan span : spans) {
|
||||
if (span.getStyle() == style) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查选中文本是否具有下划线样式
|
||||
*/
|
||||
public boolean hasUnderline() {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
UnderlineSpan[] spans = spanned.getSpans(selStart, selEnd, UnderlineSpan.class);
|
||||
return spans.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查选中文本是否具有删除线样式
|
||||
*/
|
||||
public boolean hasStrikethrough() {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
StrikethroughSpan[] spans = spanned.getSpans(selStart, selEnd, StrikethroughSpan.class);
|
||||
return spans.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用样式到选中文本
|
||||
*/
|
||||
public void applyStyle(int style) {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
StyleSpan[] spans = spanned.getSpans(selStart, selEnd, StyleSpan.class);
|
||||
|
||||
boolean hasStyle = false;
|
||||
for (StyleSpan span : spans) {
|
||||
if (span.getStyle() == style) {
|
||||
hasStyle = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasStyle) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
|
||||
builder.setSpan(new StyleSpan(style), selStart, selEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
setText(builder);
|
||||
setSelection(selStart, selEnd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除选中文本的样式
|
||||
*/
|
||||
public void removeStyle(int style) {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
StyleSpan[] spans = spanned.getSpans(selStart, selEnd, StyleSpan.class);
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
|
||||
for (StyleSpan span : spans) {
|
||||
if (span.getStyle() == style) {
|
||||
builder.removeSpan(span);
|
||||
}
|
||||
}
|
||||
|
||||
setText(builder);
|
||||
setSelection(selStart, selEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用或移除下划线样式
|
||||
*/
|
||||
public void toggleUnderline() {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
UnderlineSpan[] spans = spanned.getSpans(selStart, selEnd, UnderlineSpan.class);
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
|
||||
|
||||
if (spans.length > 0) {
|
||||
// 移除下划线
|
||||
for (UnderlineSpan span : spans) {
|
||||
builder.removeSpan(span);
|
||||
}
|
||||
} else {
|
||||
// 添加下划线
|
||||
builder.setSpan(new UnderlineSpan(), selStart, selEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
setText(builder);
|
||||
setSelection(selStart, selEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用或移除删除线样式
|
||||
*/
|
||||
public void toggleStrikethrough() {
|
||||
if (!(getText() instanceof Spanned)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int selStart = getSelectionStart();
|
||||
int selEnd = getSelectionEnd();
|
||||
if (selStart >= selEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
Spanned spanned = (Spanned) getText();
|
||||
StrikethroughSpan[] spans = spanned.getSpans(selStart, selEnd, StrikethroughSpan.class);
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
|
||||
|
||||
if (spans.length > 0) {
|
||||
// 移除删除线
|
||||
for (StrikethroughSpan span : spans) {
|
||||
builder.removeSpan(span);
|
||||
}
|
||||
} else {
|
||||
// 添加删除线
|
||||
builder.setSpan(new StrikethroughSpan(), selStart, selEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
setText(builder);
|
||||
setSelection(selStart, selEnd);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.TagColumns;
|
||||
import net.micode.notes.tool.DataUtils;
|
||||
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
|
||||
|
||||
|
||||
public class NotesListItem extends LinearLayout {
|
||||
private ImageView mAlert;
|
||||
private TextView mTitle;
|
||||
private TextView mTime;
|
||||
private TextView mCallName;
|
||||
private NoteItemData mItemData;
|
||||
private CheckBox mCheckBox;
|
||||
private ImageView mPinnedIcon; // 新增:置顶图标
|
||||
private LinearLayout mTagsLayout; // 新增:标签显示区域
|
||||
|
||||
public NotesListItem(Context context) {
|
||||
super(context);
|
||||
inflate(context, R.layout.note_item, this);
|
||||
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
|
||||
mTitle = (TextView) findViewById(R.id.tv_title);
|
||||
mTime = (TextView) findViewById(R.id.tv_time);
|
||||
mCallName = (TextView) findViewById(R.id.tv_name);
|
||||
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
|
||||
mPinnedIcon = (ImageView) findViewById(R.id.iv_pin_icon); // 新增:获取置顶图标引用
|
||||
mTagsLayout = (LinearLayout) findViewById(R.id.ll_note_tags); // 新增:获取标签显示区域引用
|
||||
}
|
||||
|
||||
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
|
||||
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
|
||||
mCheckBox.setVisibility(View.VISIBLE);
|
||||
mCheckBox.setChecked(checked);
|
||||
} else {
|
||||
mCheckBox.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mItemData = data;
|
||||
|
||||
// 设置置顶图标显示状态
|
||||
if (data.isPinned() && data.getType() == Notes.TYPE_NOTE) {
|
||||
mPinnedIcon.setVisibility(View.VISIBLE);
|
||||
mPinnedIcon.setImageResource(R.drawable.ic_pinned);
|
||||
} else {
|
||||
mPinnedIcon.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
|
||||
mCallName.setVisibility(View.GONE);
|
||||
mAlert.setVisibility(View.VISIBLE);
|
||||
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
|
||||
mTitle.setText(context.getString(R.string.call_record_folder_name)
|
||||
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
|
||||
mAlert.setImageResource(R.drawable.call_record);
|
||||
// 文件夹不显示置顶图标
|
||||
mPinnedIcon.setVisibility(View.GONE);
|
||||
// 隐藏标签显示区域
|
||||
mTagsLayout.setVisibility(View.GONE);
|
||||
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
|
||||
mCallName.setVisibility(View.VISIBLE);
|
||||
mCallName.setText(data.getCallName());
|
||||
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
|
||||
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
|
||||
if (data.hasAlert()) {
|
||||
mAlert.setImageResource(R.drawable.clock);
|
||||
mAlert.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mAlert.setVisibility(View.GONE);
|
||||
}
|
||||
// 通话记录不显示置顶图标
|
||||
mPinnedIcon.setVisibility(View.GONE);
|
||||
// 隐藏标签显示区域
|
||||
mTagsLayout.setVisibility(View.GONE);
|
||||
} else {
|
||||
mCallName.setVisibility(View.GONE);
|
||||
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
|
||||
|
||||
if (data.getType() == Notes.TYPE_FOLDER) {
|
||||
mTitle.setText(data.getSnippet()
|
||||
+ context.getString(R.string.format_folder_files_count,
|
||||
data.getNotesCount()));
|
||||
// 为普通文件夹添加文件夹图标
|
||||
mAlert.setVisibility(View.VISIBLE);
|
||||
mAlert.setImageResource(R.drawable.list_folder);
|
||||
// 文件夹不显示置顶图标
|
||||
mPinnedIcon.setVisibility(View.GONE);
|
||||
// 隐藏标签显示区域
|
||||
mTagsLayout.setVisibility(View.GONE);
|
||||
} else {
|
||||
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
|
||||
if (data.hasAlert()) {
|
||||
mAlert.setImageResource(R.drawable.clock);
|
||||
mAlert.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mAlert.setVisibility(View.GONE);
|
||||
}
|
||||
// 普通笔记:根据置顶状态显示图标
|
||||
if (data.isPinned()) {
|
||||
mPinnedIcon.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mPinnedIcon.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 显示笔记的标签
|
||||
showTags(context, data.getId());
|
||||
}
|
||||
}
|
||||
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
|
||||
|
||||
setTag(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示笔记的标签
|
||||
* @param context 上下文
|
||||
* @param noteId 笔记ID
|
||||
*/
|
||||
private void showTags(Context context, long noteId) {
|
||||
// 清空现有标签
|
||||
mTagsLayout.removeAllViews();
|
||||
|
||||
// 隐藏标签显示区域,避免空布局占用空间
|
||||
mTagsLayout.setVisibility(View.GONE);
|
||||
|
||||
// TODO: 从数据库中获取实际的标签数据
|
||||
// 暂时不添加标签,避免每次绑定都创建新视图导致卡顿
|
||||
}
|
||||
|
||||
public NoteItemData getItemData() {
|
||||
return mItemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置置顶图标可见性
|
||||
*
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
public void setPinnedIconVisibility(boolean visible) {
|
||||
if (mPinnedIcon != null) {
|
||||
mPinnedIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取置顶图标视图
|
||||
*
|
||||
* @return 置顶图标ImageView
|
||||
*/
|
||||
public ImageView getPinnedIcon() {
|
||||
return mPinnedIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新置顶状态显示
|
||||
*
|
||||
* @param pinned 是否置顶
|
||||
*/
|
||||
public void updatePinnedStatus(boolean pinned) {
|
||||
if (mPinnedIcon != null) {
|
||||
if (pinned) {
|
||||
mPinnedIcon.setVisibility(View.VISIBLE);
|
||||
mPinnedIcon.setImageResource(R.drawable.ic_pinned);
|
||||
} else {
|
||||
mPinnedIcon.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.ActionBar;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.preference.CheckBoxPreference;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.gtask.remote.GTaskSyncService;
|
||||
|
||||
|
||||
public class NotesPreferenceActivity extends PreferenceActivity {
|
||||
public static final String PREFERENCE_NAME = "notes_preferences";
|
||||
|
||||
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
|
||||
|
||||
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
|
||||
|
||||
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
|
||||
private static final String PREFERENCE_PASSWORD_LOCK_ENABLED_KEY = "pref_key_password_lock_enabled";
|
||||
private static final String PREFERENCE_PASSWORD_SET_KEY = "pref_key_password_set";
|
||||
|
||||
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
|
||||
|
||||
private static final String AUTHORITIES_FILTER_KEY = "authorities";
|
||||
|
||||
private PreferenceCategory mAccountCategory;
|
||||
|
||||
private GTaskReceiver mReceiver;
|
||||
|
||||
private Account[] mOriAccounts;
|
||||
|
||||
private boolean mHasAddedAccount;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
/* using the app icon for navigation */
|
||||
ActionBar actionBar = getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
|
||||
mReceiver = new GTaskReceiver();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
|
||||
registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
|
||||
|
||||
// 设置密码锁相关逻辑
|
||||
setupPasswordLockPreferences();
|
||||
|
||||
mOriAccounts = null;
|
||||
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
|
||||
getListView().addHeaderView(header, null, true);
|
||||
}
|
||||
|
||||
private void setupPasswordLockPreferences() {
|
||||
// 获取密码锁复选框和设置密码选项
|
||||
final CheckBoxPreference passwordLockEnabledPref = (CheckBoxPreference) findPreference(PREFERENCE_PASSWORD_LOCK_ENABLED_KEY);
|
||||
final Preference passwordSetPref = findPreference(PREFERENCE_PASSWORD_SET_KEY);
|
||||
|
||||
// 设置密码锁复选框的监听器
|
||||
passwordLockEnabledPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean isEnabled = (Boolean) newValue;
|
||||
passwordSetPref.setEnabled(isEnabled);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 设置当前状态
|
||||
passwordSetPref.setEnabled(passwordLockEnabledPref.isChecked());
|
||||
|
||||
// 设置密码选项的点击事件
|
||||
passwordSetPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent intent = new Intent(NotesPreferenceActivity.this, PasswordSetActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// need to set sync account automatically if user has added a new
|
||||
// account
|
||||
if (mHasAddedAccount) {
|
||||
Account[] accounts = getGoogleAccounts();
|
||||
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
|
||||
for (Account accountNew : accounts) {
|
||||
boolean found = false;
|
||||
for (Account accountOld : mOriAccounts) {
|
||||
if (TextUtils.equals(accountOld.name, accountNew.name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
setSyncAccount(accountNew.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (mReceiver != null) {
|
||||
unregisterReceiver(mReceiver);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void loadAccountPreference() {
|
||||
mAccountCategory.removeAll();
|
||||
|
||||
Preference accountPref = new Preference(this);
|
||||
final String defaultAccount = getSyncAccountName(this);
|
||||
accountPref.setTitle(getString(R.string.preferences_account_title));
|
||||
accountPref.setSummary(getString(R.string.preferences_account_summary));
|
||||
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (!GTaskSyncService.isSyncing()) {
|
||||
if (TextUtils.isEmpty(defaultAccount)) {
|
||||
// the first time to set account
|
||||
showSelectAccountAlertDialog();
|
||||
} else {
|
||||
// if the account has already been set, we need to promp
|
||||
// user about the risk
|
||||
showChangeAccountConfirmAlertDialog();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(NotesPreferenceActivity.this,
|
||||
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mAccountCategory.addPreference(accountPref);
|
||||
}
|
||||
|
||||
private void loadSyncButton() {
|
||||
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
|
||||
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
|
||||
|
||||
// set button state
|
||||
if (GTaskSyncService.isSyncing()) {
|
||||
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
|
||||
syncButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
|
||||
syncButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
GTaskSyncService.startSync(NotesPreferenceActivity.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
|
||||
|
||||
// set last sync time
|
||||
if (GTaskSyncService.isSyncing()) {
|
||||
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
|
||||
lastSyncTimeView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
long lastSyncTime = getLastSyncTime(this);
|
||||
if (lastSyncTime != 0) {
|
||||
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
|
||||
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
|
||||
lastSyncTime)));
|
||||
lastSyncTimeView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
lastSyncTimeView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshUI() {
|
||||
loadAccountPreference();
|
||||
loadSyncButton();
|
||||
}
|
||||
|
||||
private void showSelectAccountAlertDialog() {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
|
||||
|
||||
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
|
||||
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
|
||||
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
|
||||
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
|
||||
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
|
||||
|
||||
dialogBuilder.setCustomTitle(titleView);
|
||||
dialogBuilder.setPositiveButton(null, null);
|
||||
|
||||
Account[] accounts = getGoogleAccounts();
|
||||
String defAccount = getSyncAccountName(this);
|
||||
|
||||
mOriAccounts = accounts;
|
||||
mHasAddedAccount = false;
|
||||
|
||||
if (accounts.length > 0) {
|
||||
CharSequence[] items = new CharSequence[accounts.length];
|
||||
final CharSequence[] itemMapping = items;
|
||||
int checkedItem = -1;
|
||||
int index = 0;
|
||||
for (Account account : accounts) {
|
||||
if (TextUtils.equals(account.name, defAccount)) {
|
||||
checkedItem = index;
|
||||
}
|
||||
items[index++] = account.name;
|
||||
}
|
||||
dialogBuilder.setSingleChoiceItems(items, checkedItem,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setSyncAccount(itemMapping[which].toString());
|
||||
dialog.dismiss();
|
||||
refreshUI();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
|
||||
dialogBuilder.setView(addAccountView);
|
||||
|
||||
final AlertDialog dialog = dialogBuilder.show();
|
||||
addAccountView.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
mHasAddedAccount = true;
|
||||
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
|
||||
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
|
||||
"gmail-ls"
|
||||
});
|
||||
startActivityForResult(intent, -1);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showChangeAccountConfirmAlertDialog() {
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
|
||||
|
||||
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
|
||||
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
|
||||
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
|
||||
getSyncAccountName(this)));
|
||||
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
|
||||
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
|
||||
dialogBuilder.setCustomTitle(titleView);
|
||||
|
||||
CharSequence[] menuItemArray = new CharSequence[] {
|
||||
getString(R.string.preferences_menu_change_account),
|
||||
getString(R.string.preferences_menu_remove_account),
|
||||
getString(R.string.preferences_menu_cancel)
|
||||
};
|
||||
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == 0) {
|
||||
showSelectAccountAlertDialog();
|
||||
} else if (which == 1) {
|
||||
removeSyncAccount();
|
||||
refreshUI();
|
||||
}
|
||||
}
|
||||
});
|
||||
dialogBuilder.show();
|
||||
}
|
||||
|
||||
private Account[] getGoogleAccounts() {
|
||||
AccountManager accountManager = AccountManager.get(this);
|
||||
return accountManager.getAccountsByType("com.google");
|
||||
}
|
||||
|
||||
private void setSyncAccount(String account) {
|
||||
if (!getSyncAccountName(this).equals(account)) {
|
||||
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
if (account != null) {
|
||||
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
|
||||
} else {
|
||||
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
|
||||
}
|
||||
editor.commit();
|
||||
|
||||
// clean up last sync time
|
||||
setLastSyncTime(this, 0);
|
||||
|
||||
// clean up local gtask related info
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.GTASK_ID, "");
|
||||
values.put(NoteColumns.SYNC_ID, 0);
|
||||
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
|
||||
}
|
||||
}).start();
|
||||
|
||||
Toast.makeText(NotesPreferenceActivity.this,
|
||||
getString(R.string.preferences_toast_success_set_accout, account),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeSyncAccount() {
|
||||
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
|
||||
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
|
||||
}
|
||||
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
|
||||
editor.remove(PREFERENCE_LAST_SYNC_TIME);
|
||||
}
|
||||
editor.commit();
|
||||
|
||||
// clean up local gtask related info
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.GTASK_ID, "");
|
||||
values.put(NoteColumns.SYNC_ID, 0);
|
||||
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static String getSyncAccountName(Context context) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
|
||||
}
|
||||
|
||||
public static void setLastSyncTime(Context context, long time) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static long getLastSyncTime(Context context) {
|
||||
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
|
||||
}
|
||||
|
||||
private class GTaskReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
refreshUI();
|
||||
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
|
||||
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
|
||||
syncStatus.setText(intent
|
||||
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
Intent intent = new Intent(this, NotesListActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.tool.PasswordUtils;
|
||||
|
||||
/**
|
||||
* 密码设置界面
|
||||
* 用户可以设置新密码或修改现有密码
|
||||
*/
|
||||
public class PasswordSetActivity extends Activity implements View.OnClickListener, TextWatcher {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 标题文本
|
||||
*/
|
||||
private TextView mTitleTextView;
|
||||
|
||||
/**
|
||||
* 当前密码输入区域
|
||||
*/
|
||||
private LinearLayout mOldPasswordLayout;
|
||||
|
||||
/**
|
||||
* 当前密码输入框
|
||||
*/
|
||||
private EditText mOldPasswordEditText;
|
||||
|
||||
/**
|
||||
* 新密码输入框
|
||||
*/
|
||||
private EditText mNewPasswordEditText;
|
||||
|
||||
/**
|
||||
* 确认密码输入框
|
||||
*/
|
||||
private EditText mConfirmPasswordEditText;
|
||||
|
||||
/**
|
||||
* 密码强度提示文本
|
||||
*/
|
||||
private TextView mStrengthTextView;
|
||||
|
||||
/**
|
||||
* 错误提示文本
|
||||
*/
|
||||
private TextView mErrorTextView;
|
||||
|
||||
/**
|
||||
* 取消按钮
|
||||
*/
|
||||
private Button mCancelButton;
|
||||
|
||||
/**
|
||||
* 保存按钮
|
||||
*/
|
||||
private Button mSaveButton;
|
||||
|
||||
/**
|
||||
* 当前是否已有密码
|
||||
*/
|
||||
private boolean mHasPassword = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_password_set);
|
||||
|
||||
// 初始化视图
|
||||
initViews();
|
||||
|
||||
// 检查是否已有密码
|
||||
checkIfHasPassword();
|
||||
|
||||
// 设置点击事件监听器
|
||||
setListeners();
|
||||
|
||||
// 设置文本变化监听器
|
||||
setTextWatchers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
private void initViews() {
|
||||
mTitleTextView = findViewById(R.id.tv_title);
|
||||
mOldPasswordLayout = findViewById(R.id.ll_old_password);
|
||||
mOldPasswordEditText = findViewById(R.id.et_old_password);
|
||||
mNewPasswordEditText = findViewById(R.id.et_new_password);
|
||||
mConfirmPasswordEditText = findViewById(R.id.et_confirm_password);
|
||||
mStrengthTextView = findViewById(R.id.tv_strength);
|
||||
mErrorTextView = findViewById(R.id.tv_error);
|
||||
mCancelButton = findViewById(R.id.btn_cancel);
|
||||
mSaveButton = findViewById(R.id.btn_save);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已有密码
|
||||
*/
|
||||
private void checkIfHasPassword() {
|
||||
mHasPassword = PasswordUtils.hasPassword(this);
|
||||
|
||||
// 如果已有密码,显示当前密码输入区域
|
||||
if (mHasPassword) {
|
||||
mOldPasswordLayout.setVisibility(View.VISIBLE);
|
||||
mTitleTextView.setText(R.string.password_change_title);
|
||||
} else {
|
||||
mOldPasswordLayout.setVisibility(View.GONE);
|
||||
mTitleTextView.setText(R.string.password_set_title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置点击事件监听器
|
||||
*/
|
||||
private void setListeners() {
|
||||
mCancelButton.setOnClickListener(this);
|
||||
mSaveButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文本变化监听器
|
||||
*/
|
||||
private void setTextWatchers() {
|
||||
mNewPasswordEditText.addTextChangedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.btn_cancel) {
|
||||
// 处理取消按钮点击事件
|
||||
finish();
|
||||
} else if (id == R.id.btn_save) {
|
||||
// 处理保存按钮点击事件
|
||||
savePassword();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存密码
|
||||
*/
|
||||
private void savePassword() {
|
||||
// 重置错误提示
|
||||
hideError();
|
||||
|
||||
// 检查当前密码(如果已有密码)
|
||||
if (mHasPassword) {
|
||||
String oldPassword = mOldPasswordEditText.getText().toString().trim();
|
||||
|
||||
if (TextUtils.isEmpty(oldPassword)) {
|
||||
showError(getString(R.string.password_error_empty));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PasswordUtils.verifyPassword(this, oldPassword)) {
|
||||
showError(getString(R.string.password_error_old_wrong));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查新密码
|
||||
String newPassword = mNewPasswordEditText.getText().toString().trim();
|
||||
if (TextUtils.isEmpty(newPassword)) {
|
||||
showError(getString(R.string.password_error_empty));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length() < 4 || newPassword.length() > 16) {
|
||||
showError(getString(R.string.password_error_length));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查确认密码
|
||||
String confirmPassword = mConfirmPasswordEditText.getText().toString().trim();
|
||||
if (TextUtils.isEmpty(confirmPassword)) {
|
||||
showError(getString(R.string.password_error_empty));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newPassword.equals(confirmPassword)) {
|
||||
showError(getString(R.string.password_error_match));
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存密码
|
||||
savePasswordToPrefs(newPassword);
|
||||
|
||||
// 显示保存成功提示
|
||||
String successMessage = mHasPassword ? getString(R.string.password_success_change) : getString(R.string.password_success_set);
|
||||
Toast.makeText(this, successMessage, Toast.LENGTH_SHORT).show();
|
||||
|
||||
// 关闭当前Activity
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存密码到SharedPreferences
|
||||
* @param password 要保存的密码
|
||||
*/
|
||||
private void savePasswordToPrefs(String password) {
|
||||
try {
|
||||
PasswordUtils.savePasswordAndEnableLock(this, password);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, R.string.password_error_save, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误信息
|
||||
* @param errorMsg 错误信息
|
||||
*/
|
||||
private void showError(String errorMsg) {
|
||||
try {
|
||||
if (mErrorTextView != null) {
|
||||
mErrorTextView.setText(errorMsg);
|
||||
mErrorTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏错误信息
|
||||
*/
|
||||
private void hideError() {
|
||||
try {
|
||||
if (mErrorTextView != null) {
|
||||
mErrorTextView.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// 文本变化前的处理
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// 文本变化中的处理
|
||||
updatePasswordStrength(s.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// 文本变化后的处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新密码强度提示
|
||||
* @param password 密码
|
||||
*/
|
||||
private void updatePasswordStrength(String password) {
|
||||
if (TextUtils.isEmpty(password)) {
|
||||
mStrengthTextView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
String strength = getPasswordStrength(password);
|
||||
mStrengthTextView.setText(strength);
|
||||
mStrengthTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密码强度
|
||||
* @param password 密码
|
||||
* @return 密码强度描述
|
||||
*/
|
||||
private String getPasswordStrength(String password) {
|
||||
int length = password.length();
|
||||
|
||||
if (length < 4) {
|
||||
return getString(R.string.password_strength_weak);
|
||||
} else if (length < 8) {
|
||||
return getString(R.string.password_strength_medium);
|
||||
} else {
|
||||
return getString(R.string.password_strength_strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.widget;
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.tool.ResourceParser;
|
||||
import net.micode.notes.ui.NoteEditActivity;
|
||||
import net.micode.notes.ui.NotesListActivity;
|
||||
|
||||
public abstract class NoteWidgetProvider extends AppWidgetProvider {
|
||||
public static final String [] PROJECTION = new String [] {
|
||||
NoteColumns.ID,
|
||||
NoteColumns.BG_COLOR_ID,
|
||||
NoteColumns.SNIPPET
|
||||
};
|
||||
|
||||
public static final int COLUMN_ID = 0;
|
||||
public static final int COLUMN_BG_COLOR_ID = 1;
|
||||
public static final int COLUMN_SNIPPET = 2;
|
||||
|
||||
private static final String TAG = "NoteWidgetProvider";
|
||||
|
||||
@Override
|
||||
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||
for (int i = 0; i < appWidgetIds.length; i++) {
|
||||
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
|
||||
values,
|
||||
NoteColumns.WIDGET_ID + "=?",
|
||||
new String[] { String.valueOf(appWidgetIds[i])});
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
|
||||
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
|
||||
PROJECTION,
|
||||
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
|
||||
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLDER) },
|
||||
null);
|
||||
}
|
||||
|
||||
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
update(context, appWidgetManager, appWidgetIds, false);
|
||||
}
|
||||
|
||||
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
|
||||
boolean privacyMode) {
|
||||
for (int i = 0; i < appWidgetIds.length; i++) {
|
||||
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
|
||||
int bgId = ResourceParser.getDefaultBgId(context);
|
||||
String snippet = "";
|
||||
Intent intent = new Intent(context, NoteEditActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
|
||||
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
|
||||
|
||||
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
|
||||
if (c != null && c.moveToFirst()) {
|
||||
if (c.getCount() > 1) {
|
||||
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
|
||||
c.close();
|
||||
return;
|
||||
}
|
||||
snippet = c.getString(COLUMN_SNIPPET);
|
||||
bgId = c.getInt(COLUMN_BG_COLOR_ID);
|
||||
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
} else {
|
||||
snippet = context.getResources().getString(R.string.widget_havenot_content);
|
||||
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
|
||||
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
|
||||
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
|
||||
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
|
||||
/**
|
||||
* Generate the pending intent to start host for the widget
|
||||
*/
|
||||
PendingIntent pendingIntent = null;
|
||||
if (privacyMode) {
|
||||
rv.setTextViewText(R.id.widget_text,
|
||||
context.getString(R.string.widget_under_visit_mode));
|
||||
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
|
||||
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
} else {
|
||||
rv.setTextViewText(R.id.widget_text, snippet);
|
||||
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
|
||||
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract int getBgResourceId(int bgId);
|
||||
|
||||
protected abstract int getLayoutId();
|
||||
|
||||
protected abstract int getWidgetType();
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.widget;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.tool.ResourceParser;
|
||||
|
||||
|
||||
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
super.update(context, appWidgetManager, appWidgetIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.widget_2x;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getBgResourceId(int bgId) {
|
||||
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getWidgetType() {
|
||||
return Notes.TYPE_WIDGET_2X;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.widget;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.tool.ResourceParser;
|
||||
|
||||
|
||||
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
super.update(context, appWidgetManager, appWidgetIds);
|
||||
}
|
||||
|
||||
protected int getLayoutId() {
|
||||
return R.layout.widget_4x;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getBgResourceId(int bgId) {
|
||||
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getWidgetType() {
|
||||
return Notes.TYPE_WIDGET_4X;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue