package com.moez.QKSMS.ui;
import android.app.ActivityManager.TaskDescription;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import butterknife.Bind;
import butterknife.ButterKnife;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.moez.QKSMS.R;
import com.moez.QKSMS.common.DonationManager;
import com.moez.QKSMS.common.LiveViewManager;
import com.moez.QKSMS.common.QKRateSnack;
import com.moez.QKSMS.common.google.DraftCache;
import com.moez.QKSMS.common.utils.MessageUtils;
import com.moez.QKSMS.data.Conversation;
import com.moez.QKSMS.enums.QKPreference;
import com.moez.QKSMS.receiver.IconColorReceiver;
import com.moez.QKSMS.service.DeleteOldMessagesService;
import com.moez.QKSMS.transaction.NotificationManager;
import com.moez.QKSMS.transaction.SmsHelper;
import com.moez.QKSMS.ui.base.QKActivity;
import com.moez.QKSMS.ui.conversationlist.ConversationListFragment;
import com.moez.QKSMS.ui.dialog.DefaultSmsHelper;
import com.moez.QKSMS.ui.dialog.QKDialog;
import com.moez.QKSMS.ui.dialog.mms.MMSSetupFragment;
import com.moez.QKSMS.ui.messagelist.MessageListActivity;
import com.moez.QKSMS.ui.search.SearchActivity;
import com.moez.QKSMS.ui.settings.SettingsFragment;
import com.moez.QKSMS.ui.welcome.WelcomeActivity;
import org.ligi.snackengage.SnackEngage;
import org.ligi.snackengage.snacks.BaseSnack;
import java.util.Collection;
public class MainActivity extends QKActivity {
private final String TAG = "MainActivity";
public static long sThreadShowing;
private static final int THREAD_LIST_QUERY_TOKEN = 1701;
private static final int UNREAD_THREADS_QUERY_TOKEN = 1702;
public static final int DELETE_CONVERSATION_TOKEN = 1801;
public static final int HAVE_LOCKED_MESSAGES_TOKEN = 1802;
private static final int DELETE_OBSOLETE_THREADS_TOKEN = 1803;
public static final String MMS_SETUP_DONT_ASK_AGAIN = "mmsSetupDontAskAgain";
@Bind(R.id.root) View mRoot;
private ConversationListFragment mConversationList;
/**
* True if the mms setup fragment has been dismissed and we shouldn't show it anymore.
*/
private final String KEY_MMS_SETUP_FRAGMENT_DISMISSED = "mmsSetupFragmentShown";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
launchWelcomeActivity();
onNewIntent(getIntent());
setContentView(R.layout.activity_fragment);
setTitle(R.string.title_conversation_list);
ButterKnife.bind(this);
FragmentManager fm = getFragmentManager();
mConversationList = (ConversationListFragment) fm.findFragmentByTag(ConversationListFragment.TAG);
if (mConversationList == null) {
mConversationList = new ConversationListFragment();
}
FragmentTransaction menuTransaction = fm.beginTransaction();
menuTransaction.replace(R.id.content_frame, mConversationList, ConversationListFragment.TAG);
menuTransaction.commit();
showDialogIfNeeded(savedInstanceState);
LiveViewManager.registerView(QKPreference.BACKGROUND, this, key -> {
// Update the background color. This code is important during the welcome screen setup, when the activity
// in the ThemeManager isn't the MainActivity
mRoot.setBackgroundColor(ThemeManager.getBackgroundColor());
});
//Adds a small/non intrusive snackbar that asks the user to rate the app
SnackEngage.from(this).withSnack(new QKRateSnack().withDuration(BaseSnack.DURATION_LONG))
.build().engageWhenAppropriate();
DeleteOldMessagesService.setupAutoDeleteAlarm(this);
}
/**
* Shows at most one dialog using the intent extras and the restored state of the activity.
*
* @param savedInstanceState restored state
*/
private void showDialogIfNeeded(Bundle savedInstanceState) {
// Check if the intent has the ICON_COLOR_CHANGED action; if so, show a new dialog.
if (getIntent().getBooleanExtra(IconColorReceiver.EXTRA_ICON_COLOR_CHANGED, false)) {
// Clear the flag in the intent so that the dialog doesn't show up anymore
getIntent().putExtra(IconColorReceiver.EXTRA_ICON_COLOR_CHANGED, false);
// Display a dialog showcasing the new icon!
ImageView imageView = new ImageView(this);
PackageManager manager = getPackageManager();
try {
ComponentInfo info = manager.getActivityInfo(getComponentName(), 0);
imageView.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), info.getIconResource()));
} catch (PackageManager.NameNotFoundException ignored) {
}
new QKDialog()
.setContext(this)
.setTitle(getString(R.string.icon_ready))
.setMessage(R.string.icon_ready_message)
.setCustomView(imageView)
.setPositiveButton(R.string.okay, null)
.show();
// Only show the MMS setup fragment if it hasn't already been dismissed
} else if (!wasMmsSetupFragmentDismissed(savedInstanceState)) {
beginMmsSetup();
}
}
private boolean wasMmsSetupFragmentDismissed(Bundle savedInstanceState) {
// It hasn't been dismissed if the saved instance state isn't initialized, or is initialized
// but doesn't have the flag.
return savedInstanceState != null
&& savedInstanceState.getBoolean(KEY_MMS_SETUP_FRAGMENT_DISMISSED, false);
}
private void launchWelcomeActivity() {
if (mPrefs.getBoolean(SettingsFragment.WELCOME_SEEN, false)) {
// User has already seen the welcome screen
return;
}
Intent welcomeIntent = new Intent(this, WelcomeActivity.class);
startActivityForResult(welcomeIntent, WelcomeActivity.WELCOME_REQUEST_CODE);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
menu.clear();
showBackButton(false);
mConversationList.inflateToolbar(menu, inflater, this);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onKeyUp(KeyEvent.KEYCODE_BACK, null);
return true;
case R.id.menu_search:
startActivity(SearchActivity.class);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == WelcomeActivity.WELCOME_REQUEST_CODE) {
new DefaultSmsHelper(this, R.string.not_default_first).showIfNotDefault(null);
}
}
public static Intent createAddContactIntent(String address) {
// address must be a single recipient
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
if (SmsHelper.isEmailAddress(address)) {
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, address);
} else {
intent.putExtra(ContactsContract.Intents.Insert.PHONE, address);
intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
}
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
return intent;
}
@Override
public void onDestroy() {
super.onDestroy();
DonationManager.getInstance(this).destroy();
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (mConversationList.isShowingBlocked()) {
mConversationList.setShowingBlocked(false);
} else {
finish();
}
}
return false;
}
@Override
protected void onPause() {
super.onPause();
sThreadShowing = 0;
}
@Override
protected void onStart() {
super.onStart();
// Only mark screen if the screen is on. onStart() is still called if the app is in the
// foreground and the screen is off
// TODO this solution doesn't work if the activity is in the foreground but the lockscreen is on
if (isScreenOn()) {
SmsHelper.markSmsSeen(this);
SmsHelper.markMmsSeen(this);
NotificationManager.update(this);
}
}
@Override
protected void onResume() {
super.onResume();
sThreadShowing = 0;
NotificationManager.initQuickCompose(this, false, false);
}
/**
* MainActivity has a "singleTask" launch mode, which means that if it is currently running
* and another intent is launched to open it, instead of creating a new MainActivity it
* just opens the current MainActivity. We use this so that when you click on notifications,
* only one main activity is ever used.
* <p>
* onNewIntent() is called every time the homescreen shortcut is tapped, even if the app
* is already running in the background. It's also called when the app is launched via other
* intents
* <p>
* Docs:
* http://developer.android.com/guide/components/tasks-and-back-stack.html#TaskLaunchModes
*/
@Override
public void onNewIntent(Intent intent) {
// onNewIntent doesn't change the result of getIntent() by default, so here we set it since
// that makes the most sense.
setIntent(intent);
boolean shouldOpenConversation = intent.hasExtra(MessageListActivity.ARG_THREAD_ID);
// The activity can also be launched by clicking on the message button from the contacts app
// Check for {sms,mms}{,to}: schemes, in which case we know to open a conversation
if (intent.getData() != null) {
String scheme = intent.getData().getScheme();
shouldOpenConversation = shouldOpenConversation || scheme.startsWith("sms") || scheme.startsWith("mms");
}
if (shouldOpenConversation) {
intent.setClass(this, MessageListActivity.class);
startActivity(intent);
}
}
private void beginMmsSetup() {
if (!mPrefs.getBoolean(MMS_SETUP_DONT_ASK_AGAIN, false) &&
TextUtils.isEmpty(mPrefs.getString(SettingsFragment.MMSC_URL, "")) &&
TextUtils.isEmpty(mPrefs.getString(SettingsFragment.MMS_PROXY, "")) &&
TextUtils.isEmpty(mPrefs.getString(SettingsFragment.MMS_PORT, ""))) {
// Launch the MMS setup fragment here. This is a series of dialogs that will guide the
// user through the MMS setup process.
FragmentManager manager = getFragmentManager();
if (manager.findFragmentByTag(MMSSetupFragment.TAG) == null) {
MMSSetupFragment f = new MMSSetupFragment();
Bundle args = new Bundle();
args.putBoolean(MMSSetupFragment.ARG_ASK_FIRST, true);
args.putString(MMSSetupFragment.ARG_DONT_ASK_AGAIN_PREF, MMS_SETUP_DONT_ASK_AGAIN);
f.setArguments(args);
getFragmentManager()
.beginTransaction()
.add(f, MMSSetupFragment.TAG)
.commit();
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
FragmentManager m = getFragmentManager();
// Save whether or not the mms setup fragment was dismissed
if (m.findFragmentByTag(MMSSetupFragment.TAG) == null) {
outState.putBoolean(KEY_MMS_SETUP_FRAGMENT_DISMISSED, true);
}
}
/**
* Build and show the proper delete thread dialog. The UI is slightly different
* depending on whether there are locked messages in the thread(s) and whether we're
* deleting single/multiple threads or all threads.
*
* @param listener gets called when the delete button is pressed
* @param threadIds the thread IDs to be deleted (pass null for all threads)
* @param hasLockedMessages whether the thread(s) contain locked messages
* @param context used to load the various UI elements
*/
public static void confirmDeleteThreadDialog(final DeleteThreadListener listener, Collection<Long> threadIds,
boolean hasLockedMessages, Context context) {
View contents = View.inflate(context, R.layout.dialog_delete_thread, null);
android.widget.TextView msg = (android.widget.TextView) contents.findViewById(R.id.message);
if (threadIds == null) {
msg.setText(R.string.confirm_delete_all_conversations);
} else {
// Show the number of threads getting deleted in the confirmation dialog.
int cnt = threadIds.size();
msg.setText(context.getResources().getQuantityString(
R.plurals.confirm_delete_conversation, cnt, cnt));
}
final CheckBox checkbox = (CheckBox) contents.findViewById(R.id.delete_locked);
if (!hasLockedMessages) {
checkbox.setVisibility(View.GONE);
} else {
listener.setDeleteLockedMessage(checkbox.isChecked());
checkbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.setDeleteLockedMessage(checkbox.isChecked());
}
});
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.confirm_dialog_title)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setCancelable(true)
.setPositiveButton(R.string.delete, listener)
.setNegativeButton(R.string.cancel, null)
.setView(contents)
.show();
}
public static class DeleteThreadListener implements DialogInterface.OnClickListener {
private final Collection<Long> mThreadIds;
private final Conversation.ConversationQueryHandler mHandler;
private final Context mContext;
private boolean mDeleteLockedMessages;
public DeleteThreadListener(Collection<Long> threadIds, Conversation.ConversationQueryHandler handler, Context context) {
mThreadIds = threadIds;
mHandler = handler;
mContext = context;
}
public void setDeleteLockedMessage(boolean deleteLockedMessages) {
mDeleteLockedMessages = deleteLockedMessages;
}
@Override
public void onClick(DialogInterface dialog, final int whichButton) {
MessageUtils.handleReadReport(mContext, mThreadIds,
PduHeaders.READ_STATUS__DELETED_WITHOUT_BEING_READ, () -> {
int token = DELETE_CONVERSATION_TOKEN;
if (mThreadIds == null) {
Conversation.startDeleteAll(mHandler, token, mDeleteLockedMessages);
DraftCache.getInstance().refresh();
} else {
Conversation.startDelete(mHandler, token, mDeleteLockedMessages, mThreadIds);
}
}
);
dialog.dismiss();
}
}
}